|
| 1 | +# Ghostel |
| 2 | + |
| 3 | +Emacs terminal emulator powered by [libghostty-vt](https://ghostty.org/) — the |
| 4 | +same VT engine that drives the Ghostty terminal. |
| 5 | + |
| 6 | +Ghostel is inspired by |
| 7 | +[emacs-libvterm](https://github.com/akermu/emacs-libvterm): a native dynamic |
| 8 | +module handles terminal state and rendering, while Elisp manages the shell |
| 9 | +process, keymap, and buffer. |
| 10 | + |
| 11 | +## Requirements |
| 12 | + |
| 13 | +- Emacs 25.1+ with dynamic module support |
| 14 | +- [Zig](https://ziglang.org/) 0.14+ |
| 15 | +- macOS or Linux |
| 16 | + |
| 17 | +## Building |
| 18 | + |
| 19 | +```sh |
| 20 | +# Clone with submodule |
| 21 | +git clone --recurse-submodules https://github.com/dakra/ghostel.git |
| 22 | +cd ghostel |
| 23 | + |
| 24 | +# Build everything (libghostty-vt + ghostel module) |
| 25 | +./build.sh |
| 26 | +``` |
| 27 | + |
| 28 | +If you already have the repo, initialize the submodule and build: |
| 29 | + |
| 30 | +```sh |
| 31 | +git submodule update --init vendor/ghostty |
| 32 | +./build.sh |
| 33 | +``` |
| 34 | + |
| 35 | +## Installation |
| 36 | + |
| 37 | +Add to your Emacs config: |
| 38 | + |
| 39 | +```elisp |
| 40 | +(add-to-list 'load-path "/path/to/ghostel") |
| 41 | +(require 'ghostel) |
| 42 | +``` |
| 43 | + |
| 44 | +Then `M-x ghostel` to open a terminal. |
| 45 | + |
| 46 | +## Shell Integration |
| 47 | + |
| 48 | +For directory tracking and other features, source the appropriate shell |
| 49 | +integration script. Ghostel sets `INSIDE_EMACS=ghostel` and |
| 50 | +`EMACS_GHOSTEL_PATH` in the shell environment. |
| 51 | + |
| 52 | +**bash** — add to `~/.bashrc`: |
| 53 | +```bash |
| 54 | +[[ "$INSIDE_EMACS" = 'ghostel' ]] && source "$EMACS_GHOSTEL_PATH/etc/ghostel.bash" |
| 55 | +``` |
| 56 | + |
| 57 | +**zsh** — add to `~/.zshrc`: |
| 58 | +```zsh |
| 59 | +[[ "$INSIDE_EMACS" = 'ghostel' ]] && source "$EMACS_GHOSTEL_PATH/etc/ghostel.zsh" |
| 60 | +``` |
| 61 | + |
| 62 | +**fish** — add to `~/.config/fish/config.fish`: |
| 63 | +```fish |
| 64 | +test "$INSIDE_EMACS" = 'ghostel'; and source "$EMACS_GHOSTEL_PATH/etc/ghostel.fish" |
| 65 | +``` |
| 66 | + |
| 67 | +## Key Bindings |
| 68 | + |
| 69 | +### Terminal mode |
| 70 | + |
| 71 | +| Key | Action | |
| 72 | +|-------------|----------------------------------------| |
| 73 | +| Most keys | Sent directly to the terminal | |
| 74 | +| `C-c C-c` | Send interrupt (C-c) | |
| 75 | +| `C-c C-z` | Send suspend (C-z) | |
| 76 | +| `C-c C-d` | Send EOF (C-d) | |
| 77 | +| `C-c C-\` | Send quit (C-\) | |
| 78 | +| `C-c C-k` | Enter copy mode | |
| 79 | +| `C-y` | Yank from kill ring (bracketed paste) | |
| 80 | +| `M-y` | Yank-pop (cycle through kill ring) | |
| 81 | +| `C-c C-y` | Paste from kill ring | |
| 82 | +| `C-c C-l` | Clear scrollback | |
| 83 | +| `C-c C-q` | Send next key literally (escape hatch) | |
| 84 | +| Mouse wheel | Scroll through scrollback | |
| 85 | + |
| 86 | +Keys listed in `ghostel-keymap-exceptions` (default: `C-c`, `C-x`, `C-u`, |
| 87 | +`C-h`, `C-g`, `M-x`, `M-o`, `M-:`, `C-\`) pass through to Emacs. |
| 88 | + |
| 89 | +### Copy mode |
| 90 | + |
| 91 | +Enter with `C-c C-k`. Standard Emacs navigation works. |
| 92 | + |
| 93 | +| Key | Action | |
| 94 | +|---------------|-------------------------| |
| 95 | +| `C-SPC` | Set mark | |
| 96 | +| `M-w` / `C-w` | Copy selection and exit | |
| 97 | +| `q` | Exit without copying | |
| 98 | + |
| 99 | +Soft-wrapped newlines are automatically stripped from copied text. |
| 100 | + |
| 101 | +## Features |
| 102 | + |
| 103 | +### Terminal Emulation |
| 104 | +- Full VT terminal emulation via libghostty-vt |
| 105 | +- 256-color and RGB color support |
| 106 | +- Text attributes: bold, italic, faint, underline (single/double/curly/dotted/dashed with color), strikethrough, inverse |
| 107 | +- Cursor styles: block, bar, underline, hollow block |
| 108 | +- Alternate screen buffer (for TUI apps like htop, vim, etc.) |
| 109 | +- Scrollback buffer (configurable, default 10,000 lines) |
| 110 | + |
| 111 | +### Rendering |
| 112 | +- Incremental redraw — only dirty rows are re-rendered |
| 113 | +- Timer-based batched updates (~30fps) to avoid flicker |
| 114 | +- Cursor position updates even without cell changes |
| 115 | + |
| 116 | +### Input |
| 117 | +- Full keyboard input with GhosttyKeyEncoder (respects terminal modes) |
| 118 | +- Mouse tracking with GhosttyMouseEncoder (press, release, drag) |
| 119 | +- Focus events gated by DEC mode 1004 |
| 120 | +- Bracketed paste |
| 121 | +- Drag-and-drop (file paths and text) |
| 122 | + |
| 123 | +### Shell Integration |
| 124 | +- Directory tracking via OSC 7 |
| 125 | +- Title tracking (buffer renamed from OSC 2) |
| 126 | +- OSC 52 clipboard support (opt-in, for remote sessions) |
| 127 | +- `INSIDE_EMACS` and `EMACS_GHOSTEL_PATH` environment variables |
| 128 | + |
| 129 | +### Color Palette |
| 130 | + |
| 131 | +The 16 ANSI colors are defined as Emacs faces inheriting from `term-color-*`: |
| 132 | + |
| 133 | +``` |
| 134 | +ghostel-color-black ghostel-color-bright-black |
| 135 | +ghostel-color-red ghostel-color-bright-red |
| 136 | +ghostel-color-green ghostel-color-bright-green |
| 137 | +ghostel-color-yellow ghostel-color-bright-yellow |
| 138 | +ghostel-color-blue ghostel-color-bright-blue |
| 139 | +ghostel-color-magenta ghostel-color-bright-magenta |
| 140 | +ghostel-color-cyan ghostel-color-bright-cyan |
| 141 | +ghostel-color-white ghostel-color-bright-white |
| 142 | +``` |
| 143 | + |
| 144 | +Themes that customize `term-color-*` faces automatically apply. Customize |
| 145 | +individual faces with `M-x customize-face`. |
| 146 | + |
| 147 | +## Configuration |
| 148 | + |
| 149 | +| Variable | Default | Description | |
| 150 | +|-------------------------------|---------------------|----------------------------------------| |
| 151 | +| `ghostel-shell` | `$SHELL` | Shell program to run | |
| 152 | +| `ghostel-buffer-name` | `"*ghostel*"` | Default buffer name | |
| 153 | +| `ghostel-max-scrollback` | `10000` | Maximum scrollback lines | |
| 154 | +| `ghostel-timer-delay` | `0.033` | Redraw delay in seconds (~30fps) | |
| 155 | +| `ghostel-kill-buffer-on-exit` | `t` | Kill buffer when shell exits | |
| 156 | +| `ghostel-enable-osc52` | `nil` | Allow apps to set clipboard via OSC 52 | |
| 157 | +| `ghostel-keymap-exceptions` | `("C-c" "C-x" ...)` | Keys passed through to Emacs | |
| 158 | +| `ghostel-exit-functions` | `nil` | Hook run when the shell process exits | |
| 159 | + |
| 160 | +## Commands |
| 161 | + |
| 162 | +| Command | Description | |
| 163 | +|--------------------------------|---------------------------------------| |
| 164 | +| `M-x ghostel` | Open a new terminal | |
| 165 | +| `M-x ghostel-other` | Switch to next terminal or create one | |
| 166 | +| `M-x ghostel-clear` | Clear screen and scrollback | |
| 167 | +| `M-x ghostel-clear-scrollback` | Clear scrollback only | |
| 168 | +| `M-x ghostel-copy-mode` | Enter copy mode | |
| 169 | +| `M-x ghostel-paste` | Paste from kill ring | |
| 170 | +| `M-x ghostel-send-next-key` | Send next key literally | |
| 171 | +| `M-x ghostel-force-redraw` | Force a full terminal redraw | |
| 172 | + |
| 173 | +## Running Tests |
| 174 | + |
| 175 | +```sh |
| 176 | +emacs --batch -Q -L . -l test/ghostel-test.el -f ghostel-test-run |
| 177 | +``` |
| 178 | + |
| 179 | +## Architecture |
| 180 | + |
| 181 | +``` |
| 182 | +ghostel.el Elisp: keymap, process management, mode, commands |
| 183 | +src/module.zig Entry point: emacs_module_init, function registration |
| 184 | +src/terminal.zig Terminal struct wrapping ghostty handles |
| 185 | +src/render.zig RenderState → Emacs buffer with styled text |
| 186 | +src/input.zig Key and mouse encoding via ghostty encoders |
| 187 | +src/emacs.zig Zig wrapper for the Emacs module C API |
| 188 | +src/ghostty.zig Re-exports and constants for the ghostty C API |
| 189 | +``` |
| 190 | + |
| 191 | +## License |
| 192 | + |
| 193 | +GPL-3.0-or-later |
0 commit comments