Ghostel.el - libghostty 기반 Emacs 터미널 에뮬레이터
1 day ago
3
- Ghostel은 Ghostty와 같은 libghostty-vt 엔진을 사용하는 Emacs 터미널 에뮬레이터로, Zig 네이티브 모듈이 터미널 상태·렌더링·로컬 PTY 입출력을 맡고 Elisp가 키맵·버퍼·명령·원격 프로세스를 관리함
- Kitty 키보드·그래픽 프로토콜, OSC 8 하이퍼링크, OSC 4/10/11 색상 질의, DEC 2026 동기화 출력과 5가지 입력 모드를 지원하며 로컬 bash·zsh·fish·nushell에는 셸 통합을 자동 적용함
- Emacs 28.1 이상과 macOS·Linux·FreeBSD·네이티브 Windows를 지원하고, 처음 사용할 때 플랫폼별 사전 빌드 모듈을 자동으로 내려받을 수 있어 일반적인 설치에는 Zig 도구 체인이 필요 없음
- Apple M4 Max와 Emacs 32.0.50에서 1MB 출력을 처리한 벤치마크는 일반 ASCII 기준 Ghostel 75MB/s, vterm 18MB/s, eat 6.2MB/s를 기록했으며 네이티브 PTY는 10MB cat 작업을 약 110ms에 흡수함
- TRAMP 원격 터미널, 비밀번호 입력 보호, Kitty 인라인 이미지, 컴파일·Eshell·Comint·Evil·입력기 연동까지 제공하지만 원격 대상과 이미지 처리에는 플랫폼 및 프로토콜 제약이 있음
구성과 설치
- Ghostel은 emacs-libvterm의 2계층 설계에서 영감을 받았지만, libvterm 대신 Ghostty의 현대적인 VT 엔진을 사용함
- Zig 모듈은 터미널 상태, 렌더링, 키 인코딩, 로컬 PTY와 백그라운드 읽기를 처리함
- Elisp는 키맵, 버퍼, 명령, 프로젝트 연동과 TRAMP 원격 프로세스를 담당함
- 요구 사항은 동적 모듈을 지원하는 Emacs 28.1 이상과 macOS·Linux·FreeBSD 또는 네이티브 Windows임
- 사전 빌드 바이너리는 aarch64-macos, x86_64-macos, x86_64-linux, aarch64-linux, x86_64-freebsd, x86_64-windows, aarch64-windows용으로 제공됨
- 다른 플랫폼이나 소스 빌드에는 Zig 0.15.2가 필요함
- MELPA, Emacs 30 이상의 use-package :vc, :load-path, 수동 require 방식으로 설치할 수 있으며 M-x ghostel로 터미널을 엶
- 네이티브 모듈이 없으면 ghostel-module-auto-install 설정에 따라 다운로드 또는 소스 컴파일을 제안함
- M-x ghostel-download-module은 최소 지원 바이너리를 받고, 접두 인수를 주면 릴리스 태그를 선택할 수 있음
- M-x ghostel-module-compile은 zig build로 모듈을 빌드함
- 패키지 디렉터리가 재설치될 수 있다면 ghostel-module-directory를 별도 위치로 지정할 수 있음
- 소스 빌드는 생성된 vendor/emacs-module.h를 포함하므로 일반적으로 로컬 Emacs 헤더가 필요 없고, EMACS_INCLUDE_DIR 또는 EMACS_BIN_DIR로 헤더 위치를 바꿀 수 있음
- 컴파일된 xterm-ghostty terminfo는 etc/terminfo/에 포함되며 BSD와 ncurses에서 이식 가능한 형식임
Windows 지원
- 네이티브 Windows Emacs에서는 ConPTY로 로컬 터미널을 실행하며, 모듈 DLL의 아키텍처는 Windows가 아니라 Emacs 아키텍처와 일치해야 함
- 사전 빌드 모듈과 함께 conpty.dll, OpenConsole.exe 지원 파일을 내려받으며, 이 파일이 없으면 Windows 내장 ConPTY API로 대체함
- 정적 CRT 등 특수한 사용자 정의 Emacs 빌드는 최선 노력 방식으로만 지원함
- Windows의 TRAMP 터미널은 POSIX 원격 대상만 지원하고 PTY는 원격 호스트에 존재하며, 동적 창 크기 변경은 현재 지원하지 않음
5가지 입력 모드
- 기본 semi-char 모드는 대부분의 키를 터미널로 보내되 C-c, C-x, C-u, C-h, M-x, M-:, C-\ 같은 예외를 Emacs에 남김
- 인터럽트·중단·EOF 전송, bracketed paste, 스크롤백 삭제, 프롬프트 및 하이퍼링크 이동을 위한 키를 제공함
- char 모드는 모든 키를 터미널로 보내므로 자체적으로 C-x, M-x, C-h를 사용하는 TUI에 적합하며 M-RET만 탈출 키로 남음
- Emacs 모드는 터미널 출력을 계속 받으면서 버퍼를 읽기 전용으로 바꿔 isearch, occur, 마크와 복사 등 표준 Emacs 탐색을 허용함
- 일반 입력은 셸로 전달되지 않으며 의도적인 C-y 붙여넣기만 bracketed paste로 전송됨
- copy 모드는 터미널 갱신을 멈춰 안정적으로 선택·탐색·복사할 수 있게 함
- 복사한 텍스트에서는 소프트 래핑으로 생긴 줄바꿈을 제거함
- 기본값인 ghostel-readonly-fast-exit=t에서는 문자 입력이나 복사 후 자동으로 일반 터미널 세션으로 돌아감
- line 모드는 키를 셸로 즉시 보내지 않고 Emacs 버퍼에서 한 줄을 편집한 뒤 RET에서 한 번에 전송함
- 비동기 출력이나 새 프롬프트가 도착해도 작성 중인 입력을 스냅샷으로 보존했다가 다시 삽입함
- OSC 133이 있으면 프롬프트 경계를 사용하고, 없으면 ghostel-prompt-regexp로 경계를 찾음
- 전체 화면 TUI가 시작되면 semi-char 모드로 전환했다가 TUI 종료 후 line 모드로 복귀함
- TAB은 comint 완성과 선택적인 bash programmable completion을 사용함
- 마우스 드래그, 다중 클릭, 마크 활성화, isearch 또는 consult-line으로 라이브 입력 위치를 벗어날 때 기본적으로 copy 모드로 전환해 선택과 위치를 보호함
- TUI가 DEC 마우스 추적을 켰다면 입력 모드에서 마우스 이벤트를 해당 프로그램에 전달함
- ghostel-mouse-drag-input-mode, ghostel-mark-activation-input-mode, ghostel-point-leave-input-mode로 전환 동작을 바꿀 수 있음
터미널 기능과 렌더링
- libghostty-vt를 통해 완전한 VT 에뮬레이션, 256색과 24비트 색상, alternate screen, 여러 밑줄 스타일과 색상, 굵게·기울임·희미하게·취소선·반전, 네 가지 커서 모양과 깜빡임을 지원함
- 기본 TERM=xterm-ghostty와 번들 terminfo는 DEC 2026 동기화 출력, Kitty 키보드, true color, 색상 밑줄, 포커스 보고 등의 기능을 애플리케이션에 알림
- OSC 4/10/11 질의로 TUI가 Emacs 얼굴 색상에서 팔레트와 전경·배경색을 얻을 수 있음
- OSC 52 클립보드는 지원하지만 기본값이 비활성화돼 있어 번들 terminfo에는 Ms 기능을 광고하지 않음
- 기본 스크롤백은 5MB, 약 5,000줄이며 Emacs 버퍼로 구체화되므로 isearch와 consult-line으로 검색할 수 있음
- OSC 8 하이퍼링크 외에도 일반 HTTP·HTTPS URL과 /path/file.el:42 형태의 파일 경로를 감지해 클릭 가능한 링크로 만듦
- Ghostty 키 인코더와 Kitty 키보드 프로토콜, SGR 마우스 이벤트, DEC 1004 포커스 이벤트, 파일 경로 및 텍스트 드래그 앤드 드롭을 지원함
- 렌더러는 변경되지 않은 행을 건너뛰고 타이머로 업데이트를 배치하며 프레임 속도를 적응적으로 조절함
- 키 입력 직후 도착한 PTY 에코는 기본 33ms 타이머를 우회해 즉시 렌더링함
- ghostel-sync-theme는 Emacs 테마에 맞춰 팔레트를 다시 동기화함
- 16개 ANSI 색상은 term-color-*를 상속하는 Emacs 얼굴이며, ghostel-default로 터미널 전용 글꼴·크기·전경·배경을 설정할 수 있음
셸 통합과 Emacs 호출
- bash·zsh·fish·nushell에는 OSC 7 디렉터리 추적, OSC 133 프롬프트 이동, OSC 2 제목 추적과 ghostel_cmd를 자동 구성함
- nushell 자체 셸 통합이 OSC 7·133·2를 제공하며 ghostel.nu는 ghostel_cmd와 outbound SSH terminfo 래퍼를 추가함
- macOS 내장 bash 3.2는 자동 주입에 쓰는 ENV를 무시하므로 최신 bash를 쓰거나 ghostel.bash를 수동으로 불러와야 함
- 셸에서 Elisp를 호출하는 ghostel_cmd는 전용 OSC 52;e 확장을 사용하며 ghostel-eval-cmds 허용 목록에 있는 함수만 실행함
- 기본 허용 함수는 find-file, find-file-other-window, dired, dired-other-window, message임
- Emacs 북마크는 터미널 작업 디렉터리와 버퍼 이름을 저장함
- 기존 버퍼가 있으면 재사용하고, 없으면 저장된 디렉터리에서 새 셸을 시작함
- 스크롤백과 세션 내용은 저장하지 않음
비밀번호·클립보드·알림
- sudo, ssh, gpg, passwd 등의 비밀번호 프롬프트를 감지하면 read-passwd 를 열고 답을 PTY로 직접 보내므로 입력이 view-lossage, 최근 키 링 또는 키보드 매크로에 남지 않음
- 로컬에서는 canonical 모드와 echo 비활성화를 tcgetattr로 확인함
- 원격 셸에서만 정규식 대체 감지를 사용해 로컬 raw-mode TUI의 오탐을 줄임
- 전송 후 비밀번호 문자열은 즉시 clear-string 처리함
- ghostel-password-prompt-functions에 auth-source, KeePass, pass 등의 공급원을 추가할 수 있음
- OSC 52를 명시적으로 활성화하면 원격 프로그램이 Emacs kill ring과 시스템 클립보드를 설정할 수 있으며, 붙여넣기는 bracketed paste로 전송함
- OSC 9와 OSC 777 알림은 ghostel-notification-function으로 전달함
- 기본 핸들러는 설치돼 있으면 alert를 사용하고, 없으면 echo 영역의 message로 대체함
- OSC 9;4 진행률은 remove, set, error, indeterminate, pause 상태와 0~100 값을 전달함
Kitty 인라인 이미지
- Kitty graphics protocol의 직접 배치와 U+10EEEE Unicode-placeholder 배치를 지원해 timg, kitty +kitten icat, yazi 등의 이미지를 렌더링함
- PNG는 포함된 stb 디코더로 처리하고 RGB·RGBA·Gray·GrayAlpha 데이터는 네이티브 모듈에서 PPM으로 변환하므로 ImageMagick이 필요 없음
- CSI 14·16·18 t 크기 질의에 응답하며, ghostel-cell-pixel-scale로 물리 픽셀 비율을 설정할 수 있음
- 이미지 처리에는 다음 제약이 있음
- 알파 채널은 합성하지 않고 제거하므로 반투명 가장자리에 적합하지 않음
- Kitty source-rect 부분 자르기는 지원하지 않고 명시적 오류를 반환함
- 같은 버퍼의 여러 가상 배치는 하나의 렌더링을 공유하며 가장 최근 전송이 우선함
- 기본적으로 inline base64만 허용하고 파일·임시 파일·공유 메모리 매체는 안전을 위해 명시적으로 활성화해야 함
- 터미널당 기본 그래픽 저장 한도는 320MiB임
TRAMP와 원격 terminfo
- default-directory가 TRAMP 경로이면 해당 POSIX 호스트에서 셸을 실행하며, SSH 계열 TRAMP 방식과 docker 등의 셸을 ghostel-tramp-shells로 지정할 수 있음
- login-shell은 getent passwd로 원격 사용자의 로그인 셸을 감지함
- OSC 7 디렉터리 추적은 기존 TRAMP의 방식·사용자·멀티홉 접두사를 보존함
- Windows 원격 대상은 이 POSIX 셸·tty 경로에서 지원하지 않음
- 원격 셸 통합은 기본적으로 주입하지 않으며 두 가지 방식을 제공함
- ghostel-tramp-shell-integration=t로 임시 스크립트를 전송하고 종료 시 정리함
- 영구 호스트에는 etc/shell/ 스크립트와 선택적으로 terminfo를 직접 설치할 수 있음
- 원격에 xterm-ghostty terminfo가 없으면 터미널 열기에 실패할 수 있어 ghostel-ssh-install-terminfo가 설치를 처리함
- TRAMP 실행은 기존 연결로 임시 디렉터리에 terminfo를 전송하고 종료 시 삭제함
- 로컬 Ghostel에서 실행한 ssh는 ssh -G로 대상을 정규화하고 캐시를 조회한 뒤, 필요하면 한 번의 추가 연결로 tic -x -를 실행함
- 로컬 terminfo 해시가 캐시 키에 포함돼 libghostty 갱신 시 자동 무효화되지만, 원격에서 별도로 변경된 terminfo는 감지하지 못함
- ssh HOST cmd, 호스트가 없는 옵션 호출, 로컬에 infocmp가 없는 경우에는 설치를 건너뜀
- 원격 호스트를 수정하지 않으려면 자동 설치를 끄고 직접 terminfo를 설치할 수 있으며, ghostel-term을 xterm-256color로 바꾸면 관련 설정이 필요 없지만 Ghostty 전용 동기화 출력 고속 경로도 사라짐
Emacs 확장 기능
- evil-ghostel은 Evil의 insert·normal 상태와 터미널 커서를 동기화함
- 이동, 삽입, 삭제, 변경, 교체, bracketed paste, readline undo와 상태별 커서 모양을 지원함
- alternate screen의 vim·less·htop에는 영향을 주지 않음
- ghostel-compile은 실제 PTY에서 명령을 실행하면서 compilation-mode 형태의 헤더·푸터·오류 강조·next-error 탐색을 제공함
- 기본은 읽기 전용 탐색이며 접두 인수를 주면 대화형 입력을 전달함
- 실행 중에도 C-c C-j와 C-c C-e 또는 C-c C-t로 대화형 입력과 컴파일 탐색을 전환할 수 있음
- ghostel-compile-global-mode는 compile, recompile, project-compile과 compilation-start 호출을 Ghostel로 보냄
- grep-mode는 기본적으로 기존 구현을 유지함
- ghostel-eshell-visual-command-mode는 Eshell의 vim·htop·less 같은 시각 명령(visual command) 을 term-mode 대신 전용 Ghostel 버퍼에서 실행함
- ghostel-comint-mode는 comint 출력 스트림을 libghostty-vt로 처리해 true color, 다양한 밑줄, OSC 8 링크와 OSC 7 디렉터리 갱신을 제공함
- 전체 터미널은 아니므로 커서 배치, alternate screen, 전체 화면 다시 그리기는 버리고 htop이나 less에는 적합하지 않음
- ghostel-ime-mode는 Hangul 등 버퍼에 직접 문자를 삽입하는 Elisp 입력기의 결과를 지운 뒤 UTF-8로 PTY에 전달함
- Quail 조합 중에는 렌더링을 미뤄 버퍼가 조합 문자열을 덮어쓰지 않게 함
성능
- Apple M4 Max와 Emacs 32.0.50에서 실제 프로세스 파이프로 1MB를 보내고 약 1,000줄의 스크롤백을 유지한 결과는 다음과 같음
- 일반 ASCII: Ghostel 75MB/s, 링크 감지 비활성 Ghostel 76MB/s, vterm 18MB/s, eat 6.2MB/s, 내장 term 7.2MB/s
- URL이 많은 출력: Ghostel 36MB/s, 링크 감지 비활성 Ghostel 78MB/s, vterm 15MB/s, eat 4.5MB/s, term 5.9MB/s
- URL·파일 경로 감지는 다시 그리기 경로 밖의 통합 타이머에서 실행돼 일반 출력에서는 비용이 거의 없지만, 링크가 밀집한 출력에서는 처리량이 약 절반으로 줄어듦
- 공정한 비교를 위해 위 수치는 Emacs 프로세스 경로를 사용했으며, 기본 로컬 네이티브 PTY는 Zig 백그라운드 스레드가 출력을 읽어 지속적인 대용량 덤프에서 약 2배 빠름
- 10MB 파일을 cat한 5회 중앙값은 네이티브 Ghostel 약 110ms, Emacs PTY Ghostel 약 220ms, vterm 약 550ms, eat 약 1.8초임
- vterm과 eat은 전송 중 Emacs 메인 스레드를 동기적으로 점유하지만 Ghostel 네이티브 PTY는 백그라운드에서 파싱해 UI가 계속 반응함
- M-x ghostel-debug-typing-latency는 키별 PTY·렌더링·전체 지연의 최솟값, 중앙값, p99, 최댓값을 측정함
vterm·eat와의 차이
- vterm은 C 기반 libvterm 네이티브 모듈을 사용하고, eat은 순수 Elisp로 작성돼 빌드 없이 이식할 수 있는 대신 처리량이 낮음
- Ghostel만 Kitty 키보드·그래픽, 다섯 종류 밑줄, OSC 8, OSC 4/10/11, OSC 9·777 알림, OSC 9;4 진행률과 DEC 2026을 모두 지원함
- eat은 Sixel 이미지를 지원하지만 Kitty 이미지는 지원하지 않으며, vterm은 두 이미지 프로토콜을 모두 지원하지 않음
- Ghostel과 eat은 SGR 마우스 이벤트를 TUI에 전달하지만 vterm은 클릭을 Emacs 포인트 이동에 사용함
- Ghostel과 eat은 line 모드를 제공하고 vterm은 제공하지 않음
- Ghostel은 출력이 멈추는 copy 모드와 출력이 계속되는 Emacs 모드를 모두 제공함
- vterm은 정지형 copy 모드를 제공하고 eat의 읽기 전용 Emacs 모드는 계속 갱신됨
- Ghostel은 셸 통합을 자동 주입하지만 vterm과 eat은 수동으로 스크립트를 불러와야 함
- 비밀번호 프롬프트는 Ghostel이 자동 감지하고, eat은 수동 전송 명령만 제공하며, vterm은 입력을 별도로 가로채지 않음
내부 아키텍처
- 로컬 버퍼는 기본적으로 Zig가 PTY를 열고 자식 프로세스를 생성한 뒤 백그라운드 스레드에서 출력을 libghostty-vt로 전달함
- OSC 콜백이나 다시 그리기가 필요하면 Lisp 이벤트를 Emacs 파이프로 보내 ghostel--events-filter가 처리함
- 대량 출력이 Emacs 프로세스 필터를 바이트 단위로 통과하지 않음
- TRAMP 버퍼는 원격 생성과 파일 핸들러를 유지하기 위해 Emacs 프로세스가 출력을 받아 같은 터미널 모델에 동기적으로 전달함
- 키 입력은 libghostty 인코더를 거쳐 PTY로 즉시 쓰고, 출력은 VT 파서와 무효화 단계를 거쳐 대화형 에코면 즉시, 아니면 통합 타이머에서 Renderer.zig가 변경된 행을 갱신함
- 터미널 렌더링은 행 교체, 전체 다시 그리기, 크기 변경과 reflow, 스크롤백 추가·삭제로 Emacs 버퍼를 수정하므로 렌더러가 포인트·마크·창 위치 보존을 책임짐
- Elisp는 입력 후 라이브 뷰포트로 이동할지, 각 모드에서 탐색 상태를 유지할지 같은 정책과 사용자 의도를 결정함
- 렌더러 변화를 Elisp의 redraw 전후 휴리스틱으로 보정하면 사용자 스크롤, redisplay 보정, 크기 변경, 콘텐츠 재작성 원인을 구분하기 어려우므로 피해야 함
테스트와 라이선스
- 테스트는 ERT 기반이며 Elisp 테스트는 Zig 모듈 없이 실행할 수 있고, native 태그 테스트에는 빌드된 모듈이 필요함
- Zig 단위 테스트, 전체 테스트, Evil 확장 테스트와 빠른 벤치마크용 Makefile 대상을 제공함
- 버그와 기능 요청, 풀 리퀘스트는 GitHub 저장소에서 받으며 렌더링 문제에는 재현 명령·escape sequence·Emacs 버전·플랫폼 정보가 필요함
- M-x ghostel-debug-info로 진단 이벤트를 수집할 수 있음
- Ghostel은 GNU GPL v3 이상으로 배포되는 자유 소프트웨어임
-
Homepage
-
Tech blog
- Ghostel.el - libghostty 기반 Emacs 터미널 에뮬레이터