ugurbolat / equake

Drop-down console for (e)shell & terminal emulation

Home Page:https://gitlab.com/emacsomancer/equake

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Equake: An Emacs drop-down console

./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, 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.

Screenshots

Equake running in StumpWM

./image/equake-in-stumpwm.gif (click for higher rez video)

Equake demonstrated at emacsconf 2019

./image/emacsconf-2019-30-equake--emacsomancer.jpg

Equake running in KDE Plasma 5

./image/equake-in-kdeplasma5.gif

Installation

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)

Melpa

https://melpa.org/packages/equake-badge.svg

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

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

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

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

Usage

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

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.

(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

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

Probably adding to your ‘Rules’ something like this:

{ rule = { instance = "*EQUAKE*", class = "Emacs" },
   properties = { titlebars_enabled = false } },

Gnome Shell

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].

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.

Keybindings & other customisation

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

Changelog

v0.98

  • 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 to C-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 to C-M-_ by default.

v0.96

  • Deprecate equake-check-if-in-equake-frame-before-closing and equake-ask-before-closing-equake in favour of equake-kill-emacs-advice.
  • Fix several Windows issues.
  • Enforce frame rules by Emacs means.
  • Unify Equake tabs properties access.

v0.95

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.

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

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.

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

Credits

  • 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.

Licence

GPLv3+

https://img.shields.io/badge/License-GPL%20v3-blue.svg

About

Drop-down console for (e)shell & terminal emulation

https://gitlab.com/emacsomancer/equake

License:GNU General Public License v3.0


Languages

Language:Emacs Lisp 100.0%