크라우니터미널 네이티브 — 한글 입력(OS 한영 전환 추종) 배선
개요
사장님 확정: crowny-terminal-native(맥·윈 네이티브 PTY 터미널, 클로드코드 실작업용)는 OS의 한영 전환을 그대로 따른다 — 맥에서는 맥 입력소스, 윈도우에서는 윈도우 IME가 조합을 전담한다. 이는 실제 터미널 에뮬레이터 표준 패턴(Terminal.app/iTerm 방식)이며, 크라우니 자체 조합기(한영 로직)는 이 레이어에 관여하지 않는다(그건 브라우저 PPM 앱 쪽 담당). 이번 세션은 맥/윈/리눅스 3개 네이티브 소스를 실사해 IME 조합 경로가 실제로 OS에 위임되고 있는지 확인하고, 맥에서 발견된 실제 버그를 표준 패턴으로 수정했다.
실사 결과
맥 (src/app.m) — 버그 발견 + 수정
기존 구조는PtyView(컨테이너 NSView)가 자식 NSTextView(_textView, editable=YES)를
갖고, _textView.delegate = self + textView:shouldChangeTextInRange:replacementString:
델리게이트로 모든 텍스트 변경을 가로채 PTY로 리다이렉트하는 방식이었다. 두 가지 문제:PtyView.keyDown:은 죽은 코드였다. 클릭 시 실제 첫 리스폰더가 되는 건 (진짜
NSTextView인) _textView이지 PtyView가 아니다(PtyView는 acceptsFirstResponder를
오버라이드하지 않아 기본값 NO). 즉 화살표/기능키/Ctrl조합을 처리하던 스위치문이 실제로는
한 번도 호출되지 않았다.
shouldChangeTextInRange:replacementString:가 IME 조합(marked text) 변경까지
수정: 표준 터미널 패턴 채택. _textView를 PtyTextView : NSTextView 서브클래스로
교체하고, 이 클래스가 실제 첫 리스폰더로 남는다(변경 없음 — 애초에 그랬으므로).
keyDown:→ 화살표/기능키/Ctrl조합만[ptyOwner handleSpecialKey:event]로 즉시
[super keyDown:]
호출 → AppKit의 interpretKeyEvents:가 시스템 IME(한글 2벌식 등) 조합을 정상 처리.
insertText:replacementRange:→ 커밋 완료된 텍스트(비-IME 일반 입력 또는 조합
textStorage는 절대 직접 편집하지 않음(실제
화면은 PTY 에코를 받은 renderScreen()이 전담 — 기존 설계 불변 유지).
doCommandBySelector:→ 개행/탭/삭제(Backspace/Delete)/이동/Escape 등 비-텍스트
setMarkedText:는 오버라이드하지 않음 —NSTextView기본(super) 구현에 위임해
paste:오버라이드 — 기본NSTextView.paste:는textStorage에 직접 삽입하므로,
PtyView.renderScreen()에if (_textView.hasMarkedText) return;가드 추가 — IME
textStorage 전체 재작성을 보류해 AppKit이 그린 조합 미리보기를 PTY
출력이 지워버리지 않게 보호.윈도우 (src/app_win.c) — 이미 부분 동작 + 앵커링 보강
WM_CHAR가 이미 WideCharToMultiByte → PTY 기록으로 배선돼 있었고, Windows 기본
동작상 IME 커밋 결과는 DefWindowProc이 자동으로 WM_CHAR로 합성해 전달하므로 기능
자체는 이미 어느 정도 동작하고 있었다(코드에 WM_IME_* 처리가 전혀 없었음에도). 다만:
- 조합창(candidate/composition box) 위치를 앵커링하는 코드가 없어 기본 위치(대체로
- 커밋 경로가 OS 기본 합성(WM_CHAR)에 암묵적으로 의존해, 명시적 제어가 없었음.
WM_IME_SETCONTEXT/WM_IME_STARTCOMPOSITION에서 updateImeCompositionPos()
(신규 헬퍼, ImmSetCompositionWindow/ImmSetCandidateWindow CFS_POINT/CFS_CANDIDATEPOS)로
조합창을 실제 터미널 커서 셀(t->curCol * g_charW, 28 + t->curRow * g_charH, 28=탭바
높이)에 앵커링. WM_IME_COMPOSITION에서 GCS_RESULTSTR만 명시적으로 가로채
ImmGetCompositionStringW로 커밋 문자열을 직접 얻어 UTF-8 변환 후 PTY에 기록하고
return 0(DefWindowProc 미호출 → 기본 WM_CHAR 자동합성과 중복 기록 방지). 조합 진행
중(GCS_COMPSTR만 있고 RESULTSTR 없음)인 경우는 가로채지 않고 기본 처리로 위임해
OS 자체 조합 미리보기 UI를 그대로 사용. WM_INPUTLANGCHANGE는 상태 표시용이라 판단해
구현 보류(선택 사항, 사장님 지시대로).
imm.hinclude +imm32.lib링크(pragma comment +Makefile.win의 MinGW
MINGW_LIBS에 -limm32 추가, MSVC cl 링크줄에도 imm32.lib 추가).리눅스 (src/app_linux.c) — 실사만, 구현 보류
GTK3 +libvte(VteTerminal 위젯) 구조. on_key_press는 윈도우 레벨에 연결돼
있고 Ctrl+T/Ctrl+W/Ctrl+Shift+[/](탭 이동)/Ctrl+Shift+C/V(클립보드)만
가로채 TRUE를 반환하며, 그 외 모든 키(일반 문자·한글 조합 진행·조합 완료·화살표·
개행·삭제 포함)는 FALSE를 반환해 포커스된 VteTerminal 위젯으로 그대로 전달된다.
VteTerminal은 GtkIMContext(XIM/IBus/Fcitx — 사용자 시스템의 입력 방식 모듈에 따라
자동 선택)를 위젯 내부에 이미 완전히 구현하고 있어, 코드 변경 없이 이미 정상 동작한다
(VTE는 조합 진행 중 미리보기, 커밋, PTY 전달까지 전부 자체 처리). 결론: XIM/ibus 연동은
필요 없음(VTE가 대신함) — 문서화만 하고 구현 보류.검증
- 맥 빌드:
make app→ exit 0, 경고 0,.app재생성 완료(런타임 미기동 — 데몬/GUI
- GUI 없는 하네스(요구사항):
/tmp스크래치패드에ime_harness.m작성.
src/app.m을 -DCROWNY_HARNESS_BUILD로 컴파일(신규 매크로가 main()을 제외 —
운영 빌드는 매크로 미정의라 영향 없음, make app으로 정상 빌드 재확인)해 실제
PtyTextView/PtyView 클래스에 직접 링크. openpty()로 하네스 내부에 PTY 쌍 생성
(데몬 아님, 검증 후 즉시 종료) + cfmakeraw로 라인 discipline 변환 제거, slave
fd에서 관측(자식 셸이 실제로 받을 바이트와 동일 관측점). 8개 케이스 전부 PASS(exit 0):
ASCII 커밋, 한글 조합 완료 커밋("안녕" → UTF-8 바이트 그대로 PTY 도달), \n→\r
변환, doCommandBySelector:(개행/삭제/이동), paste:(클립보드→PTY, textStorage
우회), textStorage 불변(길이 0→0, insertText:가 문서를 건드리지 않음을 확인).
- ANSI 회귀: 기존 헤드리스 C 하네스(
/tmp/ansi_harness.c, SGR/CUP/ED·EL/DECSTBM/
- 윈도우 크로스컴파일:
x86_64-w64-mingw32-gcc(homebrew) 존재 확인,
make -f Makefile.win cross → exit 0, build/크라우니터미널.exe (PE32+ 64-bit)
정상 생성. 실기동(WM_IME 메시지 흐름)은 실제 Windows 10 1809+ 환경에서만 확인
가능 — 이 세션은 macOS라 크로스컴파일 빌드 검증까지만 수행.관련 파일
/Users/ef/crowny-terminal-native/src/app.m—PtyTextView클래스 신설(NSTextView
PtyView.handleSpecialKey:/renderScreen() 가드/main() 하네스 매크로.
/Users/ef/crowny-terminal-native/src/app_win.c—updateImeCompositionPos(),
WM_IME_SETCONTEXT/WM_IME_STARTCOMPOSITION/WM_IME_COMPOSITION 케이스,
#include <imm.h> + imm32.lib.
/Users/ef/crowny-terminal-native/src/app_linux.c— 무변경(실사만)./Users/ef/crowny-terminal-native/Makefile.win—-limm32(MinGW) /imm32.lib(MSVC)
/Users/ef/crowny-terminal-native/한선씨/입력라우팅.한선— 소스(맥/윈/리눅스) ×
hanseonc_high/crownyc 컴파일·실행 확인).
/Users/ef/crowny-terminal-native/README.md— 이번 작업 반영(갱신).- 하네스(스크래치패드, 세션 종료 시 정리 대상):
ime_harness.m,
ansi_harness.c(기존, 재사용).잔여 이슈
- 윈도우 실기동 미검증: 크로스컴파일 빌드는 성공했지만 실제 Windows 환경에서
WM_IME_COMPOSITION/한글 IME 흐름의 육안 확인은 못 했다(맥 세션 한계). 다음
Windows 세션에서 build/크라우니터미널.exe 실행 + 한글 2벌식으로 실측 요망.
- 맥 IME 후보창 위치:
NSTextView기본firstRectForCharacterRange:에 의존하는데,
textStorage를 매 PTY 출력마다 통째로 교체하는 설계상 커서가 정확히 터미널 그리드의
시각적 커서 위치와 일치하지 않을 수 있음(대체로 문서 끝 근처라 실사용상 크게 어긋나진
않음, "최소구현" 범위로 판단해 보류).
- 리눅스: 별도 조치 불요로 판단했지만, 실제 Linux 환경에서 IBus/Fcitx로 실측
- 병렬로 CrownyBrowser 세션이 별도 작업 중이라는 사전 지시에 따라 그 파일들은 전혀