equake
is a drop-down console in the style of the old (?) drop-down
‘cheat’ consoles in games like Quake, similar to Guake or Yakuake, but
written fully in Elisp.
I wanted to have a Lisp shell and a Lisp console interface to that
shell. Of course, eshell
is already 99% of this, but having a tabbed
drop-down console feels useful to me, though it may just be what I’m used
to.
Thus the motivation for its creation was to easily swap eshell
into a
workflow habituated to drop-down terminal emulators, but it can be used
also with shell
, ansi-term
, term
, vterm or rash. And different tabs
can be opened using different shells (i.e. you could have one tab running
eshell
, one running ansi-term
, and so on and so forth).
vterm
is not currently included in Emacs by default, but I would highly
recommend using it in place of any/all of ansi-term
, term
, shell
, as
it effectively subsumes and supersedes the uses of these. (In my personal
config I only have eshell
, vterm
, and rash
enabled.)
rash
is a shell implemented in Racket, and requires Racket to be
installed and then the installation of rash
itself (e.g. via raco pkg
install rash
).
In order to have a better eshell
experience, I would recommend using some
additional eshell
-related initialisation. Here is what I’m using:
https://gitlab.com/emacsomancer/init-eshell.el. You could clone this
somewhere in your load-path
and then add to your main Emacs
initialisation file:
(with-eval-after-load 'eshell (require 'init-eshell))
I’ve also been using the Plan 9-like smart display which is a built-in
option of eshell
. You can enable this by adding to your Emacs
initialisation:
(require 'eshell)
(require 'em-smart)
(add-hook 'eshell-mode-hook 'eshell-smart-initialize)
(require 'esh-module) ; require modules
(add-to-list 'eshell-modules-list 'eshell-tramp)
(This also adds the tramp module, which I recommend.)
Equake also uses built-in Emacs functionality to probe for screen sizes via
the frame.el
library, which potential means it should work
cross-platform, as frame.el
defines frame-types \'ns
(Next Step,
i.e. Mac) and \'w32
(Windows).
See further discussion at the Babbage Files blog post.
Unless you’re installing via Melpa, make sure you have the following
external dependencies installed: dash
. This might be
accomplished via use-package
, e.g.:
(use-package dash
:ensure t)
You can install this package from Melpa, including via use-package
, e.g.
(use-package equake
:ensure t
;; some examples of optional settings follow:
:custom
;; set width a bit less than full-screen (prevent 'overflow' on multi-monitor):
(equake-size-width 0.99)
;; set distinct face for Equake: white foreground with dark blue background, and different font:
:custom-face
(equake-buffer-face
((t (:inherit 'default :family "DejaVu Sans Mono" :background "#000022" :foreground "white"))))
:config
;; prevent accidental frame closure:
(advice-add #'save-buffers-kill-terminal :before-while #'equake-kill-emacs-advice)
;; binding to restore last Equake tab when viewing a non-Equake buffer
(global-set-key (kbd "C-M-^") #'equake-restore-last-etab)
;; set default shell
(setq equake-default-shell 'vterm)
;; set list of available shells
(setq equake-available-shells
'("shell"
"vterm"
"rash"
"eshell")))
quelpa-use-package can be used to install directly from this git repo:
(use-package equake
:quelpa (equake :fetcher gitlab :repo "emacsomancer/equake")
:ensure t
:config
(advice-add #'save-buffers-kill-terminal :before-while #'equake-kill-emacs-advice))
Clone the git repo somewhere and get it into your Emacs’ load-path, e.g.,
add something like this to your init.el
(assuming you put it into
~/.emacs.d/equake
):
(add-to-list 'load-path
"~/.emacs.d/equake/")
(require 'equake)
(advice-add #'save-buffers-kill-terminal :before-while #'equake-kill-emacs-advice)
Run with:
emacsclient -n -e '(equake-invoke)'
After launching an Emacs daemon of course. I recommend binding this command to a key like F12 in your DE/WM. Executing this command will create a new equake console on your screen the first time, and subsequently toggle the console (i.e. hide or show it).
[Nb: running with
emacsclient -e '(equake/emacs-dropdown-console)'
has been deprecated.]
It works with eshell
, ansi-term
, term
, shell
, vterm
, rash
. But
it was really designed to work with eshell, which is the default (although
this is configurable), because of the incredible brilliance of eshell. New
console tabs can be specified to open with a shell other than the default
shell.
Equake is designed to work with multi-screen setups, with a different set of tabs for each screen.
You’ll probably also want to configure your WM/DE to ignore the window in the task manager etc. and have no titlebar or frame. Below are some limited notes on how to do this in various environments [needs expanding]. Equake is most thoroughly tested on KDE Plasma 5 and StumpWM, but should be able to be made to work on most DEs/WMs (I welcome information on appropriate configurations for other environments).
The following is a configuration that allows for partial window splits of
the Equake frame to behave as a floating drop-down window. The following is a configuration snippet for your .stumpwmrc
/ ~/.stumpwm.d/init.lisp
that sets this up, and allows for Equake to work across groups
(‘workspaces’). (It turns out to generally work better to use Stumpwm’s
native hide-window
function rather than Emacs’s make-frame-invisible
.)
I highly recommend adopting the mouse focus behaviour shown below.
(defun calc-equake-width ()
(let ((screen-width (caddr (with-input-from-stringp (s (run-shell-command (concat emacsclient-launch " -n -e '(equake--get-monitor-property 'workarea t)'") t)) (read s))))
(desired-width-perc (read-from-string (run-shell-command (concat emacsclient-location " -n -e 'equake-size-width'") t))))
(truncate (* screen-width desired-width-perc))))
(defun calc-equake-height ()
(let ((screen-height (cadddr (with-input-from-string (s (run-shell-command (concat emacsclient-location " -n -e '(equake--get-monitor-property 'workarea t)'") t)) (read s))))
(desired-height-perc (read-from-string (run-shell-command (concat emacsclient-location " -n -e 'equake-size-height'") t))))
(truncate (* screen-height desired-height-perc))))
(setf *equake-width* 1368)
(setf *equake-height* 768)
(defcommand invoke-equake () ()
"Raise/lower Equake drop-down console."
(let* ((on-top-windows (group-on-top-windows (current-group)))
(equake-on-top (find-equake-in-group on-top-windows)))
(when (and equake-on-top (not (find-equake-globally (screen-groups (current-screen)))))
(setf (group-on-top-windows (current-group)) (remove equake-on-top on-top-windows)))
(if (and equake-on-top (eq (current-group) (window-group (find-equake-globally (screen-groups (current-screen))))))
(progn (if (eq (find-class 'float-group) (class-of (current-group)))
(when (> (length (group-windows (current-group))) 1)
(xwin-hide equake-on-top))
(progn (unfloat-window equake-on-top (current-group))
(hide-window equake-on-top))) ;; then hide Equake window via native Stumpwm method.)
(setf (group-on-top-windows (current-group)) (remove equake-on-top on-top-windows)))
(let ((found-equake (find-equake-globally (screen-groups (current-screen))))) ; Otherwise, search all groups of current screen for Equake window:
(if (not found-equake) ; If Equake cannot be found,
(progn
(run-shell-command (concat emacsclient-location " -n -e '(equake-invoke)'")) ; then invoke Equake via emacs function.
(setf *equake-height* (calc-equake-height)) ; delay calculation of height & width setting until 1st time equake invoked
(setf *equake-width* (calc-equake-width))) ; (otherwise Emacs may not be fully loaded)
(progn (unless (eq (current-group) (window-group found-equake)) ; But if Equake window is found, and if it's in a different group
(move-window-to-group found-equake (current-group))) ; move it to the current group,
(if (eq (find-class 'float-group) (class-of (current-group)))
(xwin-unhide (window-xwin found-equake) (window-parent found-equake))
(progn (unhide-window found-equake) ; unhide window, in case hidden
;; (unfloat-window found-equake (current-group)) ;; in case in floating group
(raise-window found-equake)
(float-window found-equake (current-group)))) ; float window
(float-window-move-resize (find-equake-globally (screen-groups (current-screen))) :width *equake-width* :height *equake-height*) ; set size
(focus-window found-equake)
(push found-equake (group-on-top-windows (current-group))))))))) ; make on top
(defun find-equake-in-group (windows-list)
"Search through WINDOWS-LIST, i.e. all windows of a group, for an Equake window. Sub-component of '#find-equake-globally."
(let ((current-searched-window (car windows-list)))
(if (equal current-searched-window 'nil)
'nil
(if (search "*EQUAKE*[" (window-name current-searched-window))
current-searched-window
(find-equake-in-group (cdr windows-list))))))
(defun find-equake-globally (group-list)
"Recursively search through GROUP-LIST, a list of all groups on current screen, for an Equake window."
(if (equal (car group-list) 'nil)
'nil
(let ((equake-window (find-equake-in-group (list-windows (car group-list)))))
(if equake-window
equake-window ; stop if found and return window
(find-equake-globally (cdr group-list))))))
;; Set the mouse focus policy;
(setf *mouse-focus-policy* :click) ;; options: :click, :ignore, :sloppy
systemsettings > Window Management > Window Rules
:
Click button New
In Window matching tab
:
Description
: equake rules
Window types
: Normal Window
Window title
: Substring Match : EQUAKE
In Arrangement & Access
tab:
Check: ‘Keep above’ - Force - Yes
Check: ‘Skip taskbar’ - Force - Yes
Check: ‘Skip switcher’ - Force - Yes
In Appearance & Fixes
tab:
Check: ‘No titlebar and frame’ - Force - Yes
Check: Focus stealing prevention - Force - None
Check: Focus protection - Force - Normal
Check: Accept focus - Force - Yes
Probably adding to your ‘Rules’ something like this:
{ rule = { instance = "*EQUAKE*", class = "Emacs" },
properties = { titlebars_enabled = false } },
Appears to work in both X11 and Wayland (via Xwayland). I’m not sure what the correlate of window rules is in Gnome Shell [remains to be documented].
The frame.el
library defines methods for interacting with w32
(Windows)
and ns
(NextStep/Mac), so in theory these should also work with
equake
. This has not been tested though.
C-{ | Switch to tab on left |
C-} | Switch to tab on right |
C-M-{ | Move tab one position left |
C-M-} | Move tab one position right |
C-+ | Add new tab using default shell |
C-M-+ | Add new tab with arbitrary shell |
C-| | Rename tab |
C-M-_ | Close tab (without confirmation) |
These are customisable via customize
, as are other attributes. (I suggest also adding (global-set-key (kbd "C-M-^") #'equake-restore-last-etab)
or similar to quickly switch back to your last used Equake tab in case you opened a non-Equake buffer in the Equake frame.)
You can also customise faces, e.g. via:
(set-face-attribute 'equake-buffer-face 'nil :inherit 'default :background "#000022" :foreground "white")
(set-face-attribute 'term 'nil :inherit 'default :foreground "white") ; term/ansi-term inherit the faces of their modes
(set-face-attribute 'vterm-color-default 'nil :inherit 'default :foreground "white") ; as does vterm
- The Equake frame is destroyed when the last Equake etab associated with the frame is closed, and the tab numbering reset. (Re-invoking Equake after the frame is closed thus, as usual, creates a new frame. Tab numbering starts at ‘0’ again.)
- Add a convenience function,
equake-restore-last-etab
, to switch to the last used Equake etab when an Equake frame is viewing a non-Equake buffer (e.g. user has opened a file in the Equake frame). I recommend adding a keybinding for this. Perhaps toC-M-^
(see recommended config above). - Another new convenience function,
equake-close-tab-without-query
, closes the current Equake tab (only if the current buffer is, in fact, an Equake tab) without prompting for further confirmations about running processes and so on. It’s bound toC-M-_
by default.
- Deprecate
equake-check-if-in-equake-frame-before-closing
andequake-ask-before-closing-equake
in favour ofequake-kill-emacs-advice
. - Fix several Windows issues.
- Enforce frame rules by Emacs means.
- Unify Equake tabs properties access.
Various under-the-hood improvements (thanks to Artem Yurchenko) including the ability to open of Equake frames without any pre-existing emacsclient frames; ability to create Equake frames in terminal mode; simplification of equake-invoke
function.
This version now requires at least Emacs 26.1.
Added support for vterm
and rash
.
‘Breaking’ change: set inhibit-messages-locally to default to false. You can turn this back on via customize
or (setq equake-inhibit-message-choice 't)
.
Added Stumpwm configuration details.
Added (back) a ‘non-destructive’ method of raising the Equake frame, and
made this the default. (The old behaviour can be re-enabled by setting
equake-use-frame-hide
to 'nil
, in case the make-frame-(in)visible
functions don’t work well for you.) Also added a faster method of detecting
which screen is active for multi-monitor users. This only works on X11
(i.e. not Windows/MacOS or Wayland [as far as I know, at least; you’re
welcome to test this assumption], and is not default. To enable this, set
equake-use-xdotool-probe
to 't
(and make sure xdotool
is available on
your system).
First MELPA release.
Cleaned up code (including proper implementation of tail-call
optimisation), removed unused functions, remove hard-coded hijacking of
C-x C-c
. Updated docs to include information on improving the eshell
experience.
Note, don’t use (left . 0) (top . 0)
in your launching command (as
previously advised), as this may interfere with launching pthe equake frame
on the correct screen.
Cleaned up code a bit more, removing unneeded functions. Orphaning tab
functions remain, but are not currently used. These could be useful if
repurposed to “clearing out” tabs. Still need to track down transitory
mirroring of separate equake
frames on multi-monitor.
General overall speed improvements. The multi-monitor workaround via
emacsclient -n -c -e '(equake-invoke)' -F '((title . "*transient*") (alpha . (0 . 0)) (width . (text-pixels . 0)) (height . (text-pixels . 0)) (left . 0) (top . 0))'
is now nearly as fast as running with the simpler
emacsclient -n -e '(equake-invoke)'
is. The latter is now slightly slower due to migration away from use of
make-frame-(in)visible
, and adoption of general use of delete-frame
when toggling an equake frame off. Unfortunately, make-frame-invisible
seems very buggy. Applying make-frame-invisible
to a frame once appears
to render it invisible, but Emacs still considers it to be visible, which
means that frame-visible-p
will still report the frame as being visible
and functions like make-frame-visible
and raise-frame
will have no
effect upon the frame in question. Only a second application of
make-frame-invisible
will register the frame as reportably invisible to
Emacs. This is easily enough worked-around simply by a ‘double tap’ of
make-frame-invisible
. Unfortunately, there appear to be numerous other
problems with Emacs visibility system. For instance, frames that are less
than 100% width end up re-appearing in a position other than their original
position, and frames sometimes spontaneously resize when
re-appearing. Worse yet, applying set-frame-position
on such
malpositioned frames results in significant lag.
So adopting destroy-frame
as a general solution ended up being the best
solution. This requires being able to remember the last used buffer and
also the window-buffer-history, but I had implemented these features
independently in case of accidental frame destruction.
This also means that I think I have fixed the remaining bugs in the implementation of the restoration of the last-used buffer and the frame window’s buffer-history.
There is now a better (though not perfect) solution for multi-monitor set-ups, described above. It uses an ‘emacs probe’ to determine which monitor the focus is on. It’s a bit slower than the ‘default’ method, so I’m still looking for better solutions.
I have made a number of improvements since the last major push to Gitlab. Speed is much improved, and equake now tries to restore tabs rather than orphan them when the equake frame is forcibly closed.
I’m not entirely sure how to improve multi-monitor behaviour, though I do have a couple of ideas. One is to try (again) to have equake launch with a ‘probe’ emacsclient to make sure we’re on the right screen. The other (non-exclusive) thing I plan to try is to query emacs focus and possibly raise non-active frames on the same screen (similar to how yequake does). Other suggestions welcome.
Lots of things seem to work well, but multi-monitor can still be a bit fussy: equake doesn’t always want to open on the ‘active’ monitor, and it seems to want an emacsclient frame to already be open somewhere on the screen. Each screen/monitor gets its own list of tabs. Whether this is desired behaviour or not is perhaps questionable: but I got used to the way that AwesomeWM functioned, where monitor behaved independently with its own set of virtual desktops &c., and the current equake design preserves a small measure of this behaviour.
customize
should reveal a number of customisable features, including
default shell (eshell
, shell
, ansi-term
, term
), and colours.
- This was developed in part as an emacs-internal solution to what noctuid’s tdrop application does in terms of raising/hiding frames.
- I have tried to adapt some ideas from alphapapa’s yequake package.
- Tabs inspired by terminal emulators like Yakuake.
GPLv3+