[migrated from Gitlab: https://gitlab.com/emacsomancer/equake]
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).
Additionally functionality on Linux/Unix when xprop
and wmctrl
are installed. (Skipping taskbar on X for xprop
and “Always on Top” in GNOME Shell Wayland for wmctrl
.)
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. 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-screen-dimensions ()
(mapcar (lambda (dim) (parse-integer dim))
(split-sequence:SPLIT-SEQUENCE #\x
(string-trim '(#\newline)
(run-shell-command "xrandr --current | grep '*' | uniq | awk '{print $1}'" t)))))
(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))
(equake-width (car (calc-screen-dimensions)))
(equake-height (round (* .40 (cadr (calc-screen-dimensions))))))
(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 "emacsclient -n -e '(equake-invoke)'"))
(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
Add to your configuration:
{ rule = { name = "\\*EQUAKE\\*.*",
properties = { titlebars_enabled = false, floating = true, ontop = true } },
Or, if you’re using a Fennel configuration, add:
{:rule_any {
:name [
"\\*EQUAKE\\*.*"
]}
:properties {:floating true
:titlebars_enabled false
:ontop true}}
And, importantly, you need to set equake-restore-frame-use-offset
(otherwise, for some reason the Equake frame gradually creeps up and to left as you hide and unhide it) to t
and set a horizontal and/or vertical offset in equake-restore-frame-x-offset
and/or equake-restore-frame-y-offset
in order to reposition the unhidden Equake frame, i.e. include in your init.el
something like:
(setq equake-restore-frame-use-offset t)
(setq equake-restore-frame-y-offset 20)
or else use customize
to set “Equake Restore Frame Use Offset” to “t” and “Equake Restore Frame Y Offset” to “20” (or whatever offset value).
Appears to work in both X11 and Wayland (via Xwayland). Except requires some funky workarounds for Wayland. Included is a shell script:
equake-invoke-wayland.sh
#!/bin/sh
equakestatus=$(emacsclient -n -e '(frame-live-p (alist-get (equake--get-monitor) equake--frame))')
if [ "$equakestatus" = "nil" ]; then
emacsclient -c -e "(progn (select-frame-set-input-focus (selected-frame))
(equake--transform-existing-frame-into-equake-frame)
(goto-char (1- (point-max))))"
else
emacsclient -n -e '(progn (setq equake-use-frame-hide nil)
(equake-invoke))'
fi
This uses a special call (select-frame-set-input-focus (selected-frame))
to make sure the frame is focussed (and so should appear on top of any existing windows; this is a potentially useful trick in general for summoning emacsclient frame in GNOME Shell under Wayland), and then transforms that frame into an Equake frame. Hiding the Equake frame is done through a usual equake-invoke
call, but makes sure to set equake-use-frame-hide
to nil
to destroy the frame rather than hiding it (a necessary workaround on GNOME Shell running under Wayland).
If you want “Always on Top”, make sure wmctrl
is installed.
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
Added equake-invoke.sh
shell script; this eases Equake functionality in multi-workspace/group/tab environments by “pulling” an open Equake frame into the current workspace rather than (invisibly) minimising it on the non-active workspace. Copy equake-invoke.sh
somewhere in your $PATH
and bind the equake shortcut to run it.
- Add an option
equake-display-buffer-function
to choose whether whenequake-open-non-terminal-in-new-frame
is set tot
(i.e. force opening new buffers to occur in a non-Equake frame) that buffer is opened in a window in a new frame (the default behaviour) or re-using an existing frame.
- Various bug fixes & code cleanup.
- Add an option
equake-close-frame-on-focus-loss
to close the Equake frame when it loses focus. - Add a workaround for proper function on GNOME Shell under Wayland.
- Added
equake-restore-frame-...
customisations as workaround for AwesomeWM behaviour of restoring hidden frames.
- When
equake-open-non-terminal-in-new-frame
is set tot
Equake is prevented from opening non-Equake buffers inside of an Equake frame, instead redirecting these to new frames.
- 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+