emacsomancer / equake

A drop-down console written in Emacs Lisp, designed for eshell and terminal emulation.
GNU General Public License v3.0
2 stars 0 forks source link

+TITLE: Equake: An Emacs drop-down console

+AUTHOR: Benjamin Slade

[migrated from Gitlab: https://gitlab.com/emacsomancer/equake]

[[./image/equake.png]]

=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=, [[https://github.com/akermu/emacs-libvterm][vterm]] or [[http://rash-lang.org/][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][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:

+begin_src elisp

(with-eval-after-load 'eshell (require 'init-eshell))

+end_src

I've also been using the [[https://www.masteringemacs.org/article/complete-guide-mastering-eshell#plan-9-smart-shell][Plan 9-like smart display]] which is a built-in option of =eshell=. You can enable this by adding to your Emacs initialisation:

+begin_src elisp

(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)

+end_src

(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 [[https://babbagefiles.xyz/equake-elisp-console/][the Babbage Files blog post]].

** Equake demonstrated at emacsconf 2019 [[https://media.emacsconf.org/2019/30.html][./image/emacsconf-2019-30-equake--emacsomancer.jpg]]

** Equake running in KDE Plasma 5 [[./image/equake-in-kdeplasma5.gif]]

[[https://melpa.org/#/equake][file:https://melpa.org/packages/equake-badge.svg]]

You can install this package from Melpa, including via =use-package=, e.g.

+begin_src elisp

(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")))

+end_src

** Quelpa [[https://framagit.org/steckerhalter/quelpa-use-package][quelpa-use-package]] can be used to install directly from this git repo:

+BEGIN_SRC elisp

(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))

+END_SRC

** Manual 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=):

+BEGIN_SRC elisp

(add-to-list 'load-path "~/.emacs.d/equake/") (require 'equake) (advice-add #'save-buffers-kill-terminal :before-while #'equake-kill-emacs-advice)

+END_SRC

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

+BEGIN_SRC shell

emacsclient -e '(equake/emacs-dropdown-console)'

+END_SRC

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 [[http://www.howardism.org/Technical/Emacs/eshell-fun.html][incredible]] [[https://www.masteringemacs.org/article/complete-guide-mastering-eshell][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).

** Stumpwm 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.

+begin_src lisp

(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

+end_src

** In KDE Plasma 5 =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

** AwesomeWM Add to your configuration:

+BEGIN_SRC lua

{ rule = { name = "\EQUAKE\*.", properties = { titlebars_enabled = false, floating = true, ontop = true } },

+END_SRC

Or, if you're using a [[https://fennel-lang.org/][Fennel]] configuration, add:

+begin_src fennel

 {:rule_any {
             :name [
              "\\*EQUAKE\\*.*"
               ]}
     :properties {:floating true 
                  :titlebars_enabled false
                  :ontop true}}

+end_src

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:

+begin_src elisp

(setq equake-restore-frame-use-offset t) (setq equake-restore-frame-y-offset 20)

+end_src

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). ** Gnome Shell Appears to work in both X11 and Wayland (via Xwayland). Except requires some funky workarounds for Wayland. Included is a shell script:

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

+end_src

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.

** Outside of Linux/BSD (i.e. non-X11/Wayland) 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.

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:

+begin_src elisp

(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

+end_src

This version now requires at least Emacs 26.1. v0.90 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)=. v0.86 Added Stumpwm configuration details. v0.85 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). v0.8 First MELPA release. v0.73 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. v0.51 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. v0.50 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. v0.49 General overall speed improvements. The multi-monitor workaround via

+begin_src emacs-lisp

emacsclient -n -c -e '(equake-invoke)' -F '((title . "transient") (alpha . (0 . 0)) (width . (text-pixels . 0)) (height . (text-pixels . 0)) (left . 0) (top . 0))'

+end_src

is now nearly as fast as running with the simpler

+begin_src emacs-lisp

emacsclient -n -e '(equake-invoke)'

+end_src

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. v0.45 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. v0.4 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 [[https://github.com/alphapapa/yequake][yequake]] does). Other suggestions welcome. ** v0.3 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.

[[https://www.gnu.org/licenses/gpl-3.0][https://img.shields.io/badge/License-GPL%20v3-blue.svg]]