Turbo Vision 2.0 - 현대적인 포트
9 hours ago
3
- 고전 텍스트 UI 프레임워크를 Linux와 Windows에서 다시 활용할 수 있게 옮기면서, UTF-8과 확장 색상까지 포함한 공통 개발 기반을 제공함
- 기존 Turbo Vision 앱의 소스 코드 수준 호환성을 최대한 유지하면서도, char 기반 API를 계속 써서 플랫폼별 터미널 차이를 라이브러리 내부에서 흡수함
- Unicode 입력과 표시를 프레임워크 전반에 통합해 이중 폭 문자, 결합 문자, 멀티바이트 단축키, UTF-8 기반 편집과 그리기를 함께 처리할 수 있음
- 겹치는 창, 메뉴, 대화상자, 입력창, 스크롤바 같은 기본 위젯을 갖추고, 이벤트 루프, 타이머, 클립보드, 마우스 휠, 리사이즈, 대용량 파일 편집 같은 현대적 동작도 지원함
- 16색 중심의 과거 API를 넘어 24비트 색상, xterm-256 팔레트, 시스템 클립보드, 여러 콘솔 환경 대응까지 묶어, 오래된 TUI 코드와 현대 터미널 환경을 이어 주는 기반이 됨
프로젝트 성격과 핵심 방향
- Turbo Vision 2.0의 현대적 포트로, 고전적인 텍스트 UI 프레임워크를 Linux와 Windows에서 다시 활용할 수 있게 하면서 UTF-8과 확장 색상까지 끌어들임
- 초기에는 Linux 지원 추가와 기존 DOS/Windows 동작 유지, 오래된 Turbo Vision 앱의 소스 코드 수준 호환성 보존에 초점을 맞춤
- 이 호환성을 위해 일부 Borland C++ RTL 함수도 직접 구현함
- 2020년 7~8월 사이 기존 구조 안에 완전한 Unicode 지원을 통합했고, 그 과정에서 Turbo 텍스트 편집기도 작성함
- 애플리케이션이 터미널 기능 차이나 직접적인 터미널 I/O 처리를 우회하지 않도록 감싸서, 플랫폼별 차이를 라이브러리 쪽에서 흡수함
- Linux와 Windows에서 같은 코드로 텍스트 UI를 작성하는 데 초점을 두며, wchar_t나 TCHAR 대신 계속 char 기반 API를 유지함
왜 유용한가
- 위젯 클래스를 다수 제공해 크기 조절 가능한 겹치는 창, 풀다운 메뉴, 대화상자, 버튼, 스크롤바, 입력창, 체크박스, 라디오 버튼 등을 바로 사용할 수 있음
- 자체 위젯을 만들 때도 이벤트 디스패치나 전각 Unicode 문자 표시 같은 공통 처리를 다시 작성할 부담이 줄어듦
- 각 환경에서 가능한 한 같은 결과를 내도록 처리함
- Linux 콘솔에서 밝은 배경색이 blink 속성에 의존하는 경우도 라이브러리가 대신 처리해 줌
- Microsoft RTL의 setlocale UTF-8 지원을 활용해 Windows에서도 std::ifstream f("コンピュータ.txt"); 같은 코드가 의도대로 동작할 수 있음
- Windows에서는 RTL이 파일명을 시스템 인코딩으로 즉시 변환함
Unicode 설계와 텍스트 처리
- UTF-8을 입력과 표시의 기본 인코딩으로 채택함
- Borland C++로 빌드할 때는 Unicode를 지원하지 않지만, API 확장은 인코딩 비종속 코드를 작성할 수 있게 설계됨
- KeyDownEvent에는 기존 charScan.charCode 외에 text[4]와 textLength가 추가되어 UTF-8 입력 바이트열을 직접 전달함
- charScan.charCode에는 하위 호환성을 위해 여전히 CP437 기준 문자가 들어감
- getText()로 string view를 받아 쓰는 방식을 권장함
- 입력 예시는 Unicode와 구형 코드가 함께 동작하는 방식을 그대로 보여 줌
- ñ 입력 시 charCode=164 ('ñ'), text={'\xC3','\xB1'}, textLength=2가 됨
- € 입력 시 CP437에 없어 keyCode=0x0, charCode=0, text={'\xE2','\x82','\xAC'}, textLength=3이 됨
- 단축키 입력 시 textLength=0으로 비어 있음
- TScreenCell은 UTF-8 코드포인트와 굵게, 밑줄, 기울임 같은 확장 속성을 담을 수 있음
- ASCII 제어 문자는 코드 페이지 문자처럼 처리됨
- 유효한 UTF-8은 그대로 표시되고, 유효하지 않은 UTF-8은 코드 페이지 문자로 처리됨
- 확장 ASCII와 UTF-8을 섞을 수 있어 하위 호환성에는 유리하지만 예기치 않은 결과도 생길 수 있음
- 이중 폭 문자와 결합 문자도 따로 처리함
- 결합 문자는 앞 문자 위에 겹쳐짐
- ZERO WIDTH JOINER U+200D는 항상 생략됨
- 터미널이 wcwidth 기준 문자 폭을 존중하면 눈에 띄는 그래픽 깨짐은 거의 없음
- Wide character display에서 예시를 볼 수 있음
Unicode API와 표준 뷰 지원
-
Unicode 인식 그리기 API
- TDrawBuffer::moveChar, putChar는 char c를 코드 페이지 문자로 처리함
- moveStr, moveCStr는 TStringView를 받아 Unicode 규칙으로 문자열을 처리함
- maxStrWidth는 바이트 수가 아니라 컬럼 수 기준임
- strIndent도 바이트가 아니라 컬럼 수 기준이라 가로 스크롤에 쓸 수 있음
- 반환값은 복사된 텍스트의 표시 폭임
- moveBuf는 TStringView 도입 전 null-terminated가 아닌 문자열을 처리하던 함수라 유지됨
- cstrlen(TStringView s)는 ~를 제외한 표시 길이, strwidth(TStringView s)는 ~를 포함한 표시 길이를 반환함
-
draw() 단순화와 버그 감소
- 기존 TFileViewer::draw()는 부분 문자열을 임시 버퍼에 복사한 뒤 moveStr를 호출했지만, 이 방식은 buffer overrun 위험과 멀티바이트 경계 문제를 안고 있었음
- 수정 후에는 b.moveStr(0, p, c, size.x, delta.x); 한 줄로 처리해 중간 복사를 없앰
- delta.x를 컬럼 기준으로 바로 적용할 수 있음
- 최대 size.x 컬럼만 복사하므로 바이트 수와 경계 조건을 직접 계산할 필요가 줄어듦
-
Unicode 표시를 지원하는 표준 뷰
- TStaticText, TFrame, TStatusLine, THistoryViewer, THelpViewer, 8c7dac2a, 20f331e3, TListViewer, TMenuBox, TTerminal, TOutlineViewer, tvdemo의 TFileViewer, tvdir의 TFilePane에서 Unicode 표시를 지원함
- 일부 뷰는 가로 스크롤과 워드 래핑까지 함께 처리함
-
Unicode 입력까지 처리하는 뷰
- TInputLine, cb489d42, TEditor, TMenuView의 단축키에서 Unicode 사용자 입력까지 처리함
- TEditor 인스턴스는 기본적으로 UTF-8 모드이며 Ctrl+P로 single-byte 모드로 전환 가능함
- 이 전환은 문서 내용을 바꾸지 않고 표시 방식과 입력 인코딩만 바꿈
- TStatusLine, TButton 등의 강조된 키 단축키는 지원되지 않음
-
메뉴와 상태줄의 다국어 문자열
- ~Ñ~ello, 階~毎~料入報最..., 五劫~の~擦り切れ, העברית ~א~ינטרנט, ~Alt-Ç~ Exit 같은 문자열을 메뉴와 상태줄에서 그대로 사용할 수 있음
- 결과 화면은 Unicode Hello에서 확인 가능함
플랫폼별 동작 모델
-
Unix
- Ncurses 기반 터미널 지원을 사용함
- X10, SGR 마우스 인코딩, xterm의 modifyOtherKeys, Paul Evans의 fixterms, Kitty의 keyboard protocol, WSL Conpty의 win32-input-mode, far2l, Linux 콘솔의 TIOCLINUX 수정자와 GPM 마우스를 지원함
- 프로그램이 비정상 종료되기 전에 터미널 상태를 복구하는 커스텀 시그널 핸들러를 둠
- stderr가 tty이면 화면 훼손을 막기 위해 버퍼에 저장했다가 종료나 일시중지 시 출력함
- 버퍼 크기 제한이 있어 가득 차면 stderr 쓰기가 실패할 수 있음
- 전부 보존하려면 2> 리다이렉션을 권장함
- TERM, COLORTERM, ESCDELAY, TVISION_USE_STDIO 환경 변수를 읽음
- COLORTERM=truecolor 또는 24bit이면 24비트 색상을 가정함
- ESCDELAY 기본값은 10ms임
- TVISION_USE_STDIO가 비어 있지 않으면 /dev/tty 대신 stdin과 stdout으로 I/O를 수행함
-
Windows
- Win32 Console API와만 호환됨
- 이를 지원하지 않는 터미널 에뮬레이터에서는 별도 콘솔 창을 자동으로 띄움
- 애플리케이션 배치는 콘솔 버퍼가 아니라 콘솔 창 크기를 기준으로 하며, 종료나 일시중지 때 콘솔 버퍼를 복원함
- Borland C++가 아닐 때는 콘솔 코드 페이지를 시작 시 UTF-8로 바꾸고 종료 시 복원함
- Microsoft C 런타임도 UTF-8 모드로 바꿔 개발자가 wchar_t 변형을 직접 쓸 필요를 줄여 줌
- 레거시 모드와 비트맵 폰트 조합에서는 Unicode 표시가 깨질 수 있으며, 이를 감지하면 Consolas 또는 Lucida Console로 폰트 변경을 시도함
-
공통 동작 개선
- 24비트 색상, stdin/stdout/stderr 리다이렉션과의 공존, 32비트 help 파일 호환성을 제공함
- TVISION_MAX_FPS 환경 변수로 최대 갱신률을 제어함
- 기본값은 60임
- 0은 제한을 비활성화함
- -1은 THardwareInfo::screenWrite 호출마다 즉시 그리게 만듦
- 중간 마우스 버튼, 마우스 휠, 최대 32767 행/열 화면 크기, 리사이즈 이벤트를 지원함
- 창은 왼쪽 아래 모서리에서도 크기 조절 가능하고, 중간 버튼으로 빈 영역을 잡아 끌 수 있음
- 메뉴는 부모 항목을 다시 클릭해 닫을 수 있고, 스크롤바는 드래그 중 페이지 스크롤과 휠 입력에 반응함
- TInputLine은 포커스 이동 시 텍스트를 불필요하게 스크롤하지 않음
- TFileViewer와 TEditor는 LF 줄바꿈을 지원하며, TEditor는 저장 시 기존 줄바꿈을 유지하고 새 파일은 기본적으로 CRLF를 사용함
- TEditor는 우클릭 메뉴, 중간 버튼 드래그 스크롤, 단어 단위 삭제 키, Home 키 개선, 64 KiB 초과 파일 지원까지 포함함
- tvdemo에는 이벤트 뷰어 애플릿과 배경 패턴 변경 옵션이 들어 있음
이벤트 루프와 API 변화
- 화면 쓰기 버퍼링을 사용하며, 보통 활성 이벤트 루프 한 번당 한 번씩 터미널로 전송함
- 바쁜 루프에서 즉시 갱신이 필요하면 TScreen::flushScreen()을 호출할 수 있음
- TEventQueue::waitForEvents(int timeoutMs)는 입력 이벤트를 기다리며, 대기 중에도 TScreen::flushScreen()으로 화면 갱신을 수행함
- TProgram::getEvent()는 기본값 20인 eventTimeoutMs로 이를 호출해 100% CPU 바쁜 루프를 피함
- TEventQueue::wakeUp()은 다른 스레드에서 이벤트 루프를 깨우는 thread-safe 메서드임
- TView::getEvent(TEvent &, int timeoutMs)로 뷰 단위 타임아웃 대기를 둘 수 있음
- setTimer, killTimer가 추가되어 타이머 만료 시 evBroadcast와 cmTimerExpired를 발생시킴
- periodMs가 음수면 1회성 타이머로 자동 정리됨
- 타이머 이벤트는 TProgram::idle()에서 생성되어 키보드나 마우스 이벤트가 없을 때만 처리됨
- TDrawBuffer는 더 이상 고정 길이 배열이 아니며, 범위를 벗어나는 접근을 막아 줌
- TRect의 move, grow, intersect, Union은 TRect&를 반환해 체이닝 가능함
- TApplication은 dosShell(), cascade(), tile()을 기본 제공하고 cmDosShell, cmCascade, cmTile을 기본 처리함
- TStringView, TSpan<T>, TDrawSurface, TSurfaceView, TClipboard 같은 타입이 추가됨
- THardwareInfo, TScreen, TEventQueue는 main 이전이 아니라 첫 TApplication 생성 시 초기화됨
- 입력 확장도 함께 들어감
- evMouseWheel, mwUp, mwDown, mwLeft, mwRight
- mbMiddleButton, meTripleClick
- evMouseUp.buttons에 해제된 버튼 정보 유지
- hotKeyStr, getAltCharStr, getCtrlCharStr로 멀티바이트 단축키 구현 가능함
- TKey로 Shift+Alt+Up 같은 새 키 조합 정의 가능함
- TInputLine은 ilMaxBytes, ilMaxWidth, ilMaxChars의 limitMode를 지원함
클립보드 연동
-
시스템 클립보드 통합
- 기존에는 TEditor::clipboard 정적 멤버를 통한 내부 클립보드만 있었고, TEditor만 이를 사용할 수 있었음
- 새 TClipboard 클래스로 시스템 클립보드에 접근하며, 접근할 수 없으면 내부 클립보드로 대체함
-
지원 환경
- Windows, WSL, macOS에서는 기본 지원됨
- macOS를 제외한 Unix에서는 외부 의존성이 필요함
- 원격 실행에서는 ssh -X의 X11 forwarding, far2l와 putty4far2l, OSC 52 지원 터미널에서 동작할 수 있음
- 일부 다른 터미널은 복사만 지원함
- 터미널 에뮬레이터의 Ctrl+Shift+V나 Cmd+V 붙여넣기는 별도로 항상 가능함
-
API와 붙여넣기 처리
- <tvision/tv.h> 포함 전에 Uses_TClipboard를 정의하면 TClipboard를 사용할 수 있음
- setText(TStringView text)는 시스템 클립보드 내용을 설정하고, 접근할 수 없으면 내부 클립보드를 사용함
- requestText()는 시스템 클립보드 내용을 비동기 요청하고, 이후 일반 evKeyDown 이벤트 형태로 받음
- 붙여넣기 텍스트는 keyDown.controlKeyState의 kbPaste 플래그로 일반 키 입력과 구분 가능함
- 긴 붙여넣기가 키 입력 수천 회처럼 처리되는 비효율을 줄이기 위해 TView::textEvent(...)가 추가됨
- 연속된 evKeyDown 텍스트를 사용자 버퍼로 모아 읽음
- 더 이상 텍스트가 아니면 putEvent()로 다음 루프에 넘김
-
표준 명령 연동
- TEditor와 TInputLine은 cmCut, cmCopy, cmPaste 명령에 반응함
- 애플리케이션은 상태줄 등에 kbCtrlX, kbCtrlC, kbCtrlV를 각 명령에 연결해야 함
- 포커스와 선택 상태에 따라 관련 명령을 자동으로 활성화하거나 비활성화함
확장 색상 모델과 호환성
- Turbo Vision API는 기존 16색을 넘어 확장 색상을 지원함
- 4비트 BIOS color attributes
- 24비트 RGB
- 8비트 xterm-256color palette index
- terminal default color
- 터미널이 특정 색상 형식을 지원하지 않으면 Turbo Vision이 양자화해 표시함
- 현대 플랫폼에서는 uchar 대신 TColorAttr, ushort 대신 TAttrPair를 도입함
- TPalette도 TColorAttr 배열을 사용함
- TView::mapColor는 virtual이 되어 계층형 팔레트 시스템을 우회한 색 지정이 가능해짐
- TColorBIOS, TColorRGB, TColorXTerm, TColorDesired로 색 표현 계층을 나눔
- TColorAttr는 foreground, background, style 비트마스크를 가짐
- 스타일은 slBold, slItalic, slUnderline, slBlink, slReverse, slStrike를 포함함
- slReverse는 신뢰도가 낮아 reverseAttribute(TColorAttr attr) 사용을 권장함
- TView에서 확장 색상을 쓰는 방식은 세 가지임
- mapColor를 오버라이드해 인덱스별로 하드코딩
- draw()에서 TDrawBuffer에 직접 TColorAttr 전달
- 애플리케이션 팔레트 자체 수정
- TScreen::screenMode로 디스플레이 능력을 노출함
- smMono는 monocolor임
- smBW80는 grayscale임
- smCO80는 최소 16색임
- smColor256은 최소 256색임
- smColorHigh는 그보다 많은 색, 예를 들어 24비트 색상을 뜻함
- 하위 호환성은 #ifdef 없는 공통 API 유지에 맞춰 설계됨
- Borland C++에서는 TColorAttr, TAttrPair가 각각 uchar, ushort로 typedef됨
- 현대 플랫폼에서는 기존 uchar, ushort 자리를 대체할 수 있음
- 레거시 코드는 그대로 컴파일되지만, non-BIOS 색상 속성은 uchar나 ushort로 암묵 변환되는 순간 손실될 수 있음
- draw 메서드에서 ushort를 팔레트 인덱스 쌍과 색상 속성 쌍에 함께 쓰던 코드는 그대로 동작하지만, 확장 색상을 살리려면 변수 선언을 ushort에서 TAttrPair로 바꾸면 됨
- TColorDialog는 다시 설계되지 않아 런타임의 확장 색상 선택기로는 사용할 수 없음
빌드, 배포, 통합
-
Linux와 Unix 계열
- CMake와 GCC/Clang으로 정적 라이브러리 libtvision.a를 빌드할 수 있음
- hello, tvdemo, tvedit, tvdir, mmenu, palette, tvhc Help Compiler도 함께 생성됨
- 요구사항은 C++14 지원 컴파일러, libncursesw, 선택 항목인 libgpm으로 정리됨
- 런타임 클립보드 연동에는 xsel, xclip, wl-clipboard가 쓰임
- 일부 환경에서는 -ltinfow가 필요할 수 있으며, 그렇지 않으면 실행 시 segmentation fault가 날 수 있음
-
Windows와 다른 툴체인
- MSVC 빌드는 대상 아키텍처별 별도 빌드 디렉터리를 사용하며 결과물은 tvision.lib와 예제 앱으로 생성됨
- TV_USE_STATIC_RTL 옵션으로 Microsoft 런타임 정적 링크가 가능함
- /MT와 /MD, 디버그와 비디버그 바이너리는 서로 섞어 링크할 수 없음
- <tvision/tv.h> 사용에는 /permissive-, /Zc:__cplusplus 플래그가 필요하며, CMake 서브모듈 방식이면 자동으로 활성화됨
- 적절한 MSVC 버전과 설정이면 Windows XP도 가능하다고 적혀 있음
- MinGW는 Linux와 비슷하게 CMake로 빌드하며, 컴파일러가 지원하면 Windows XP 이상에서 실행 가능함
- Borland C++로도 DOS 또는 Windows 라이브러리 빌드는 가능하지만 Unicode는 지원하지 않음
- Borland C++ 환경에서는 winevdm 사용이 대안으로 제시됨
-
프로젝트 통합 방식과 배포 상태
- CMake에서는 find_package(tvision CONFIG) 또는 add_subdirectory(tvision) 두 방식을 제공함
- 두 방식 모두 include 경로와 Ncurses, GPM 등 필요한 링크를 자동으로 처리함
- vcpkg에 tvision 포트가 있음
- 현재 stable release는 없으며 최신 커밋 기준 업그레이드 중 발견한 문제 보고를 권장함
- Unix 계열은 데모 앱을 직접 빌드해야 함
- Windows 바이너리는 Actions에서 제공됨
- examples-dos32.zip: Borland C++ 빌드 32비트 실행 파일, Unicode 미지원
- examples-x86.zip: MSVC 빌드 32비트 실행 파일, Windows Vista 이상 필요
- examples-x64.zip: MSVC 빌드 64비트 실행 파일, x64 Windows Vista 이상 필요
예제, 문서, 활용 사례
-
Homepage
-
Tech blog
- Turbo Vision 2.0 - 현대적인 포트