I keep many screen session running in the backgroud, which I access with M-x eshell-command "screen -r <myscreen>". Before I kill the *screen* buffer, I have to detach the session with C-a d. It would be nice to have that integrated into kill-buffer via kill-buffer-hook, but I don't know how because the *screen* buffer is read-only, so insert doesn't work.

My non-working idea is:

(add-hook `term-mode-hook (add-hook 'kill-buffer-hook (insert (kbd "C-a d"))))

What's the proper elisp command to send control-sequence inputs to a terminal via a hook?

  • I tried C-a d in a term-mode buffer and it is not bound to anything by default. What Emacs function are you intending to call when you press C-a d? Also, what steps -- how -- do you "kill the *screen* buffer"? Finally, why does your question mix and match using an eshell-command with a term-mode buffer -- aren't they two distinctly separate animals?
    – lawlist
    May 20, 2020 at 20:59
  • @lawlist AFAIU eshell employs term buffers to handle certain commands which it knows need a proper terminal emulator; so I think eshell is mostly irrelevant here in practice.
    – phils
    May 20, 2020 at 22:49
  • @lawlist C-a d is the key sequence to detach a screen session. I use eshell because it can't be run from shell in Emacs. True that eshell is mostly irrelevant here, but not knowing the solution, I thought it best to give as much detail as reasonable.
    – petermao
    May 22, 2020 at 5:58

2 Answers 2


An elisp insert into the terminal buffer does not communicate with the terminal process at all. (term-send-raw-string (kbd "C-a d")) is probably what you want to do.

Something like this?

(defun my-screen-detach ()
  "Send 'C-a d' to the terminal."
  (if (derived-mode-p 'term-mode)
      (when (process-live-p (get-buffer-process (current-buffer)))
        (message "Sending screen detach key sequence.")
        (term-send-raw-string (kbd "C-a d")))
    (user-error "Not a `term-mode' buffer.")))

(defun my-term-exec-hook ()
  "Custom `term-exec-hook' behaviours."
  ;; If "screen" was the command being run in the terminal, then
  ;; arrange to send a 'detach' screen command if we kill the
  ;; buffer.
  (let ((proc (get-buffer-process (current-buffer))))
    ;; (message "%S" (process-command proc))
    (if (member "/usr/bin/screen" (process-command proc))
        (add-hook 'kill-buffer-hook 'my-screen-detach nil :local)
      (remove-hook 'kill-buffer-hook 'my-screen-detach :local))))

(add-hook 'term-exec-hook 'my-term-exec-hook)
  • I think ideally you would use a process sentinel to trigger the behaviour, rather than assuming kill-buffer-hook is sufficient. That complicates things more, though (partly because processes can only have one sentinel, so you need to take more care). nadvice.el possibly makes that relatively simple to do nowadays, though.
    – phils
    May 20, 2020 at 23:39
  • I am wondering, though -- does killing the screen process not implicitly cause that 'detach'? Does it not clean up after itself in that scenario? (I don't use screen, so I don't entirely understand the issue.)
    – phils
    May 20, 2020 at 23:41
  • Indeed, on Ubuntu, the detach happens cleanly when I kill the buffer, but my recollection is that on CentOS, I had some trouble re-attaching screens after killing non-detached sessions. Regarding your solution, it almost works -- its just that the hook executes after the buffer is already closed, so I still get the "Buffer...has a running process" check before and a "error in process filter: Selecting deleted buffer" after. Thank you -- this gets me on track to a solution. I'll check out nadvice.el.
    – petermao
    May 22, 2020 at 6:11
  • kill-buffer-hook runs before the buffer is killed. It has to. "The buffer being killed is current while the hook is running." (Which isn't to say that there isn't an issue; just that it's not on that particular account.) The other aspects are presumably a different question to this one.
    – phils
    May 22, 2020 at 11:04

@phils is right about screen implicitly detaching when its buffer is killed. This does, in fact, work cleanly under Ubuntu, CentOS and XQuartz (& probably elsewhere as well). I found that to get the behavior I was looking for, all I really needed was to disable the exit query when screen is started. Taking many cues from phils, here is my solution:

(defun my-term-exec-hook ()
  "Custom `term-exec-hook' behaviors."
  ;; suppress query on exit for screen
  (let ((proc (get-buffer-process (current-buffer))))
    (if (member "/usr/bin/screen" (process-command proc))
        (set-process-query-on-exit-flag proc nil))))

(add-hook 'term-exec-hook 'my-term-exec-hook)

many thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.