- Khôi phục khả năng sử dụng framework giao diện văn bản cổ điển trên Linux và Windows, đồng thời cung cấp nền tảng phát triển chung với cả UTF-8 và màu mở rộng
- Giữ mức tương thích mã nguồn cao nhất có thể với các ứng dụng Turbo Vision hiện có, đồng thời tiếp tục dùng API dựa trên
char để hấp thụ khác biệt terminal theo từng nền tảng bên trong thư viện
- Tích hợp nhập và hiển thị Unicode trên toàn bộ framework để xử lý đồng thời ký tự rộng gấp đôi, ký tự kết hợp, phím tắt nhiều byte, chỉnh sửa và vẽ dựa trên UTF-8
- Cung cấp widget cơ bản như cửa sổ chồng lớp, menu, hộp thoại, ô nhập liệu, thanh cuộn, đồng thời hỗ trợ các hành vi hiện đại như vòng lặp sự kiện, bộ hẹn giờ, clipboard, bánh xe chuột, thay đổi kích thước và chỉnh sửa tệp lớn
- Vượt qua API cũ xoay quanh 16 màu để gom cả màu 24-bit, bảng màu xterm-256, clipboard hệ thống và hỗ trợ nhiều môi trường console, tạo thành nền tảng kết nối mã TUI cũ với môi trường terminal hiện đại
Tính chất dự án và định hướng cốt lõi
- Là bản port hiện đại của Turbo Vision 2.0, giúp tái sử dụng framework giao diện văn bản cổ điển trên Linux và Windows, đồng thời đưa vào cả UTF-8 và màu mở rộng
- Ban đầu tập trung vào việc bổ sung hỗ trợ Linux, giữ nguyên hành vi DOS/Windows hiện có và bảo toàn tính tương thích ở cấp mã nguồn với các ứng dụng Turbo Vision cũ
- Để phục vụ mức tương thích này, một số hàm RTL của Borland C++ cũng được tự triển khai trực tiếp
- Trong khoảng tháng 7–8 năm 2020, dự án đã tích hợp hỗ trợ Unicode đầy đủ vào cấu trúc hiện có, và trong quá trình đó cũng viết ra trình soạn thảo văn bản Turbo
- Thư viện được bọc để ứng dụng không phải lách qua khác biệt về tính năng terminal hay tự xử lý I/O terminal trực tiếp, nhờ đó hấp thụ khác biệt nền tảng ở phía thư viện
- Tập trung vào việc viết giao diện văn bản bằng cùng một mã trên Linux và Windows, đồng thời tiếp tục duy trì API dựa trên
char thay vì wchar_t hay TCHAR
Vì sao hữu ích
- Cung cấp nhiều lớp widget để có thể dùng ngay các thành phần như cửa sổ chồng lớp có thể đổi kích thước, menu thả xuống, hộp thoại, nút bấm, thanh cuộn, ô nhập liệu, checkbox và radio button
- Khi tự tạo widget riêng, cũng giảm gánh nặng phải viết lại các xử lý dùng chung như phân phối sự kiện hay hiển thị ký tự Unicode toàn chiều rộng
- Xử lý để cho ra kết quả giống nhau nhiều nhất có thể trên từng môi trường
- Ngay cả khi màu nền sáng trên Linux console phụ thuộc vào thuộc tính blink, thư viện cũng sẽ xử lý thay
- Tận dụng hỗ trợ UTF-8 của
setlocale trong Microsoft RTL để trên Windows, mã như std::ifstream f("コンピュータ.txt"); có thể hoạt động đúng như mong đợi
- Trên Windows, RTL sẽ chuyển ngay tên tệp sang bảng mã hệ thống
Thiết kế Unicode và xử lý văn bản
- Chọn UTF-8 làm mã hóa mặc định cho nhập liệu và hiển thị
- Tương thích với kiểu dữ liệu
char * hiện có và trùng với mã hóa I/O terminal, giúp giảm các bước chuyển đổi không cần thiết
- Cũng phù hợp với định hướng của UTF-8 Everywhere Manifesto
- Khi build bằng Borland C++, dự án không hỗ trợ Unicode, nhưng các mở rộng API được thiết kế để có thể viết mã độc lập với mã hóa
- Trong
KeyDownEvent, ngoài charScan.charCode hiện có còn bổ sung text[4] và textLength để truyền trực tiếp chuỗi byte đầu vào UTF-8
- Trong
charScan.charCode, ký tự theo chuẩn CP437 vẫn tiếp tục được lưu để đảm bảo tương thích ngược
- Khuyến nghị dùng
getText() để lấy string view
- Các ví dụ nhập liệu cho thấy rõ cách Unicode và mã cũ cùng hoạt động
- Khi nhập
ñ, sẽ có charCode=164 ('ñ'), text={'\xC3','\xB1'}, textLength=2
- Khi nhập
€, do không có trong CP437 nên keyCode=0x0, charCode=0, text={'\xE2','\x82','\xAC'}, textLength=3
- Khi nhập phím tắt,
textLength=0 nên để trống
- TScreenCell có thể chứa codepoint UTF-8 cùng các thuộc tính mở rộng như in đậm, gạch chân và in nghiêng
- Ký tự điều khiển ASCII được xử lý như ký tự code page
- UTF-8 hợp lệ sẽ được hiển thị nguyên trạng, còn UTF-8 không hợp lệ sẽ được xử lý như ký tự code page
- Có thể trộn ASCII mở rộng với UTF-8 nên thuận lợi cho tương thích ngược, nhưng cũng có thể tạo ra kết quả ngoài ý muốn
- Ký tự rộng gấp đôi và ký tự kết hợp cũng được xử lý riêng
- Ký tự kết hợp sẽ được chồng lên ký tự đứng trước
ZERO WIDTH JOINER U+200D luôn bị lược bỏ
- Nếu terminal tôn trọng độ rộng ký tự theo
wcwidth thì hầu như sẽ không có lỗi hiển thị đồ họa đáng замет
- Có thể xem ví dụ trong Wide character display
API Unicode và hỗ trợ các view tiêu chuẩn
-
API vẽ có nhận biết Unicode
TDrawBuffer::moveChar, putChar xử lý char c như ký tự code page
moveStr, moveCStr nhận TStringView và xử lý chuỗi theo quy tắc Unicode
maxStrWidth dựa trên số cột chứ không phải số byte
strIndent cũng dựa trên số cột thay vì byte nên có thể dùng cho cuộn ngang
- Giá trị trả về là độ rộng hiển thị của phần văn bản đã được sao chép
moveBuf được giữ lại vì đây là hàm từng dùng để xử lý chuỗi không null-terminated trước khi có TStringView
cstrlen(TStringView s) trả về độ dài hiển thị không tính ~, còn strwidth(TStringView s) trả về độ dài hiển thị có tính ~
-
Đơn giản hóa draw() và giảm lỗi
TFileViewer::draw() trước đây sao chép chuỗi con vào bộ đệm tạm rồi gọi moveStr, nhưng cách này tiềm ẩn nguy cơ buffer overrun và vấn đề ranh giới đa byte
- Sau khi sửa, chỉ cần một dòng
b.moveStr(0, p, c, size.x, delta.x); để xử lý, loại bỏ khâu sao chép trung gian
- Có thể áp dụng
delta.x trực tiếp theo đơn vị cột
- Chỉ sao chép tối đa
size.x cột nên bớt cần tự tính số byte và điều kiện biên
-
Các view tiêu chuẩn hỗ trợ hiển thị Unicode
TStaticText, TFrame, TStatusLine, THistoryViewer, THelpViewer, 8c7dac2a, 20f331e3, TListViewer, TMenuBox, TTerminal, TOutlineViewer, TFileViewer của tvdemo, TFilePane của tvdir đều hỗ trợ hiển thị Unicode
- Một số view còn xử lý cả cuộn ngang và word wrapping
-
Các view xử lý cả nhập liệu Unicode
TInputLine, cb489d42, TEditor, phím tắt của TMenuView đều xử lý được cả đầu vào Unicode từ người dùng
- Các instance
TEditor mặc định ở chế độ UTF-8 và có thể chuyển sang chế độ single-byte bằng Ctrl+P
- Việc chuyển đổi này không thay đổi nội dung tài liệu mà chỉ đổi cách hiển thị và mã hóa đầu vào
- Phím tắt được nhấn mạnh như trong
TStatusLine, TButton không được hỗ trợ
-
Chuỗi đa ngôn ngữ trong menu và thanh trạng thái
- Có thể dùng nguyên trạng các chuỗi như
~Ñ~ello, 階~毎~料入報最..., 五劫~の~擦り切れ, העברית ~א~ינטרנט, ~Alt-Ç~ Exit trong menu và thanh trạng thái
- Có thể xem màn hình kết quả tại Unicode Hello
Mô hình hoạt động theo nền tảng
-
Unix
- Sử dụng hỗ trợ terminal dựa trên Ncurses
- Hỗ trợ mã hóa chuột X10, SGR, modifyOtherKeys của xterm, fixterms của Paul Evans, keyboard protocol của Kitty,
win32-input-mode của WSL Conpty, far2l, modifier TIOCLINUX của console Linux và chuột GPM
- Có trình xử lý tín hiệu tùy chỉnh để khôi phục trạng thái terminal trước khi chương trình kết thúc bất thường
- Nếu
stderr là tty thì sẽ lưu vào bộ đệm để tránh làm hỏng màn hình, rồi in ra khi thoát hoặc tạm dừng
- Có giới hạn kích thước bộ đệm nên khi đầy, việc ghi vào
stderr có thể thất bại
- Nếu muốn giữ lại toàn bộ thì nên dùng chuyển hướng
2>
- Đọc các biến môi trường
TERM, COLORTERM, ESCDELAY, TVISION_USE_STDIO
- Nếu
COLORTERM=truecolor hoặc 24bit thì giả định hỗ trợ màu 24-bit
- Giá trị mặc định của
ESCDELAY là 10ms
- Nếu
TVISION_USE_STDIO không rỗng thì sẽ thực hiện I/O bằng stdin và stdout thay vì /dev/tty
-
Windows
- Chỉ tương thích với Win32 Console API
- Trên các terminal emulator không hỗ trợ API này, chương trình sẽ tự động mở một cửa sổ console riêng
- Bố cục ứng dụng dựa trên kích thước cửa sổ console chứ không phải buffer console, và sẽ khôi phục buffer console khi thoát hoặc tạm dừng
- Nếu không phải Borland C++ thì sẽ đổi code page của console sang UTF-8 khi khởi động và khôi phục lại khi thoát
- Cũng chuyển Microsoft C runtime sang chế độ UTF-8 để giảm nhu cầu lập trình viên phải tự dùng trực tiếp các biến thể
wchar_t
- Với tổ hợp chế độ legacy và font bitmap, việc hiển thị Unicode có thể bị lỗi; nếu phát hiện tình huống này thì sẽ thử đổi font sang
Consolas hoặc Lucida Console
- Có thể xem ví dụ trong photo
-
Cải tiến hành vi chung
- Cung cấp màu 24-bit, khả năng cùng tồn tại với chuyển hướng
stdin/stdout/stderr, và khả năng tương thích với file help 32-bit
- Dùng biến môi trường
TVISION_MAX_FPS để điều khiển tốc độ làm mới tối đa
- Mặc định là
60
0 sẽ vô hiệu hóa giới hạn
-1 khiến chương trình vẽ ngay mỗi khi gọi THardwareInfo::screenWrite
- Hỗ trợ nút chuột giữa, bánh xe chuột, kích thước màn hình tối đa 32767 hàng/cột, và sự kiện thay đổi kích thước
- Cửa sổ có thể đổi kích thước từ cả góc dưới bên trái, và có thể kéo vùng trống bằng nút chuột giữa
- Menu có thể đóng bằng cách bấm lại vào mục cha, còn thanh cuộn phản hồi với cuộn theo trang và thao tác bánh xe trong lúc kéo
TInputLine không cuộn văn bản không cần thiết khi chuyển focus
TFileViewer và TEditor hỗ trợ xuống dòng LF; TEditor giữ nguyên kiểu xuống dòng hiện có khi lưu, còn file mới mặc định dùng CRLF
TEditor còn bao gồm menu chuột phải, cuộn kéo bằng nút giữa, phím xóa theo từ, cải tiến phím Home, và hỗ trợ file lớn hơn 64 KiB
tvdemo có applet xem sự kiện và tùy chọn đổi mẫu nền
Vòng lặp sự kiện và thay đổi API
- Sử dụng bộ đệm ghi màn hình, và thông thường chỉ gửi ra terminal một lần cho mỗi lượt của vòng lặp sự kiện đang hoạt động
- Nếu cần cập nhật ngay trong vòng lặp bận, có thể gọi
TScreen::flushScreen()
TEventQueue::waitForEvents(int timeoutMs) sẽ chờ sự kiện đầu vào, đồng thời trong lúc chờ vẫn cập nhật màn hình bằng TScreen::flushScreen()
TProgram::getEvent() gọi hàm này với eventTimeoutMs mặc định là 20 để tránh vòng lặp bận 100% CPU
TEventQueue::wakeUp() là phương thức thread-safe để đánh thức vòng lặp sự kiện từ luồng khác
TView::getEvent(TEvent &, int timeoutMs) cho phép chờ timeout theo từng view
- Đã thêm
setTimer, killTimer, sẽ phát sinh evBroadcast và cmTimerExpired khi timer hết hạn
- Nếu
periodMs âm thì timer sẽ là loại một lần và tự động được dọn dẹp
- Sự kiện timer được tạo trong
TProgram::idle() và chỉ được xử lý khi không có sự kiện bàn phím hoặc chuột
TDrawBuffer không còn là mảng độ dài cố định nữa, và giúp ngăn truy cập vượt phạm vi
TRect với move, grow, intersect, Union trả về TRect& nên có thể chaining
TApplication cung cấp sẵn dosShell(), cascade(), tile() và xử lý mặc định cmDosShell, cmCascade, cmTile
- Đã thêm các kiểu như
TStringView, TSpan<T>, TDrawSurface, TSurfaceView, TClipboard
THardwareInfo, TScreen, TEventQueue được khởi tạo khi tạo TApplication đầu tiên, thay vì trước main
- Phần mở rộng đầu vào cũng được bổ sung
evMouseWheel, mwUp, mwDown, mwLeft, mwRight
mbMiddleButton, meTripleClick
evMouseUp.buttons giữ lại thông tin về nút đã được nhả
hotKeyStr, getAltCharStr, getCtrlCharStr cho phép triển khai phím tắt đa byte
- Có thể định nghĩa tổ hợp phím mới như
Shift+Alt+Up bằng TKey
TInputLine hỗ trợ limitMode của ilMaxBytes, ilMaxWidth, ilMaxChars
Tích hợp clipboard
-
Tích hợp clipboard hệ thống
- Trước đây chỉ có clipboard nội bộ thông qua thành viên tĩnh
TEditor::clipboard, và chỉ TEditor mới có thể sử dụng nó
- Lớp
TClipboard mới cho phép truy cập clipboard hệ thống, và sẽ dùng clipboard nội bộ nếu không thể truy cập
-
Môi trường hỗ trợ
- Được hỗ trợ mặc định trên Windows, WSL và macOS
- Trên Unix, ngoại trừ macOS, cần phụ thuộc bên ngoài
- Khi chạy từ xa, có thể hoạt động với X11 forwarding của
ssh -X, far2l và putty4far2l, hoặc terminal hỗ trợ OSC 52
- Một số terminal khác chỉ hỗ trợ sao chép
- Thao tác dán bằng
Ctrl+Shift+V hoặc Cmd+V của terminal emulator vẫn luôn khả dụng riêng biệt
-
API và xử lý dán
- Có thể dùng
TClipboard nếu định nghĩa Uses_TClipboard trước khi include <tvision/tv.h>
setText(TStringView text) đặt nội dung clipboard hệ thống, và sẽ dùng clipboard nội bộ nếu không thể truy cập
requestText() yêu cầu bất đồng bộ nội dung clipboard hệ thống, rồi nhận lại dưới dạng sự kiện evKeyDown thông thường
- Có thể phân biệt văn bản được dán với nhập phím thông thường bằng cờ
kbPaste trong keyDown.controlKeyState
- Để giảm sự kém hiệu quả khi các đoạn dán dài bị xử lý như hàng nghìn lần nhấn phím,
TView::textEvent(...) đã được thêm vào
- Gom các văn bản
evKeyDown liên tiếp để đọc vào bộ đệm người dùng
- Khi không còn là văn bản nữa thì chuyển sang vòng lặp kế tiếp bằng
putEvent()
-
Tích hợp lệnh chuẩn
TEditor và TInputLine phản hồi các lệnh cmCut, cmCopy, cmPaste
- Ứng dụng cần liên kết
kbCtrlX, kbCtrlC, kbCtrlV với từng lệnh, chẳng hạn trên thanh trạng thái
- Tự động bật hoặc tắt các lệnh liên quan tùy theo trạng thái focus và chọn
Mô hình màu mở rộng và tính tương thích
- API Turbo Vision hỗ trợ màu mở rộng vượt ra ngoài 16 màu truyền thống
- Thuộc tính màu BIOS 4-bit
- RGB 24-bit
- Chỉ mục bảng màu xterm-256color 8-bit
- Màu mặc định của terminal
- Nếu terminal không hỗ trợ một định dạng màu cụ thể, Turbo Vision sẽ lượng tử hóa để hiển thị
- Trên nền tảng hiện đại, TColorAttr được đưa vào thay cho
uchar, và TAttrPair thay cho ushort
TPalette cũng dùng mảng TColorAttr
TView::mapColor trở thành virtual, cho phép chỉ định màu mà không cần đi qua hệ thống palette phân cấp
TColorBIOS, TColorRGB, TColorXTerm, TColorDesired chia tách các tầng biểu diễn màu
TColorAttr có bitmask foreground, background, style
- Các style gồm
slBold, slItalic, slUnderline, slBlink, slReverse, slStrike
slReverse có độ tin cậy thấp nên khuyến nghị dùng reverseAttribute(TColorAttr attr)
- Có ba cách dùng màu mở rộng trong
TView
- Override
mapColor để hardcode theo từng chỉ mục
- Truyền trực tiếp
TColorAttr vào TDrawBuffer trong draw()
- Chỉnh sửa chính palette của ứng dụng
TScreen::screenMode biểu thị khả năng hiển thị
smMono là monocolor
smBW80 là grayscale
smCO80 là tối thiểu 16 màu
smColor256 là tối thiểu 256 màu
smColorHigh là nhiều màu hơn mức đó, ví dụ màu 24-bit
- Tính tương thích ngược được thiết kế để duy trì API chung không cần
#ifdef
- Trên Borland C++,
TColorAttr và TAttrPair lần lượt được typedef thành uchar và ushort
- Trên nền tảng hiện đại, chúng có thể thay thế các vị trí
uchar và ushort cũ
- Mã legacy vẫn biên dịch nguyên trạng, nhưng thuộc tính màu non-BIOS có thể bị mất ngay khi bị chuyển đổi ngầm sang
uchar hoặc ushort
- Mã trong phương thức
draw vốn dùng ushort cho cả cặp chỉ mục palette và cặp thuộc tính màu vẫn hoạt động như cũ, nhưng để giữ được màu mở rộng thì chỉ cần đổi khai báo biến từ ushort sang TAttrPair
TColorDialog chưa được thiết kế lại, nên không thể dùng làm bộ chọn màu mở rộng ở runtime
Xây dựng, phát hành, tích hợp
-
Linux và họ Unix
- Có thể build thư viện tĩnh
libtvision.a bằng CMake và GCC/Clang
- Đồng thời cũng tạo ra
hello, tvdemo, tvedit, tvdir, mmenu, palette và Help Compiler tvhc
- Yêu cầu gồm trình biên dịch hỗ trợ C++14,
libncursesw và tùy chọn libgpm
- Tích hợp clipboard lúc runtime sử dụng
xsel, xclip, wl-clipboard
- Trong một số môi trường có thể cần
-ltinfow, nếu không khi chạy có thể gặp segmentation fault
-
Windows và các toolchain khác
- Bản build MSVC dùng thư mục build riêng theo từng kiến trúc đích, và đầu ra được tạo thành
tvision.lib cùng các ứng dụng ví dụ
- Tùy chọn
TV_USE_STATIC_RTL cho phép liên kết tĩnh runtime của Microsoft
- Không thể liên kết lẫn lộn
/MT và /MD, cũng như binary debug và non-debug
- Để dùng
<tvision/tv.h> cần các cờ /permissive-, /Zc:__cplusplus, và nếu dùng submodule CMake thì chúng sẽ được tự động bật
- Tài liệu ghi rằng Windows XP cũng khả dụng nếu dùng đúng phiên bản và cấu hình MSVC
- MinGW build bằng CMake tương tự Linux, và nếu trình biên dịch hỗ trợ thì có thể chạy trên Windows XP trở lên
- Cũng có thể build thư viện DOS hoặc Windows bằng Borland C++, nhưng không hỗ trợ Unicode
- Trong môi trường Borland C++, winevdm được đề xuất như một phương án thay thế
-
Cách tích hợp vào dự án và tình trạng phát hành
- Trong CMake, có hai cách là
find_package(tvision CONFIG) hoặc add_subdirectory(tvision)
- Cả hai cách đều tự động xử lý đường dẫn include và các liên kết cần thiết như Ncurses, GPM
- Có port
tvision trên vcpkg
- Hiện chưa có stable release, và tác giả khuyến khích báo cáo các vấn đề phát hiện được khi nâng cấp theo commit mới nhất
- Trên họ Unix, cần tự build ứng dụng demo
- Binary cho Windows được cung cấp tại Actions
examples-dos32.zip: file thực thi 32-bit build bằng Borland C++, không hỗ trợ Unicode
examples-x86.zip: file thực thi 32-bit build bằng MSVC, yêu cầu Windows Vista trở lên
examples-x64.zip: file thực thi 64-bit build bằng MSVC, yêu cầu Windows Vista x64 trở lên
Ví dụ, tài liệu, trường hợp sử dụng
1 bình luận
Ý kiến trên Hacker News
Thật vui khi thấy kho lưu trữ này lên trang nhất, và hiện giờ tôi đang tự làm một wrapper cho chính kho này
Tôi đang chạy Turbo Vision trên macOS qua .Net, và nó mang lại cảm giác khá kỳ diệu
Tôi đang bổ sung API cấp cao hơn, đồng thời bao lại hoặc cải thiện palette API vốn khá lỗi thời, và cũng thêm cả layout
Hiện vẫn đang làm rất tích cực trong một kho riêng tư; hôm nay thì chỉnh palette dựa trên surface nơi control được đặt, ngày mai lại tinh chỉnh phần khác, kiểu như vậy
Vẫn còn việc phải làm như dọn dẹp layout, thêm các control cơ bản còn thiếu theo tiêu chuẩn ngày nay
Trước đây tôi cũng đã dùng Terminal.Gui, nhưng có lẽ vì đang chuyển sang v2 nên khá khó dùng mà không dính bug, và Claude cũng cho thấy rất rõ không nên làm gì khi tạo thư viện TUI mà không tính đến terminal thực tế
Vì thế tôi đã nghĩ sẽ thật tuyệt nếu có một Turbo Vision hiện đại, rồi tình cờ phát hiện kho này, và thấy nó còn hỗ trợ cả Unicode nên thực sự rất biết ơn
https://www.remobjects.com/elements/oxygene/
https://www.remobjects.com/elements/
Tôi cũng đang làm một .NET wrapper, có lẽ tiến độ kém hơn, nhưng muốn mô phỏng API Windows Forms giống nhất có thể và thậm chí thêm cả TUI designer kéo-thả
Ví dụ ở đây: https://github.com/brianluft/terminalforms/tree/main/src/TerminalFormsDemo
Phần lớn công việc tích hợp C++ rắc rối được xử lý ở đây: https://github.com/brianluft/terminalforms/tree/main/src/tfcore
Tôi export các hàm C đơn giản để có thể gọi bằng P/Invoke, còn phía C# thì chủ yếu tập trung vào tổ chức lớp
Ban đầu tôi cố ép rằng mọi thứ làm được trong C++ cũng phải làm được trong C#, nhưng nó trở nên quá phức tạp; thậm chí tôi còn dùng placement new để nhét đối tượng C++ vào buffer C#, gần như đạt tới mức kế thừa lớp C++ từ phía C#, rồi thiết kế sụp đổ
Cuối cùng tôi chuyển sang cách tiếp cận trực diện hơn, ít linh hoạt hơn nhưng đơn giản hơn rất nhiều, và để sự linh hoạt ở phía C#
Tôi tò mò hệ thống P/Invoke của bạn được tổ chức như thế nào
Nhờ vậy mà có lẽ tôi không còn thử mấy chuyện viển vông như viết app cho GEOS hay tham gia đội Hurd một người nữa
Tôi có dùng Terminal.Gui, nhưng phía TV cuốn hút hơn nên từng nghĩ tới chuyện làm wrapper, và nếu được công khai thì tôi thật sự rất muốn xem
Sự nghiệp lập trình của tôi đúng nghĩa là bắt đầu từ thùng rác vào thập niên 90
Tôi nhặt được một cuốn sách Turbo Vision mà ai đó vứt đi, và lập tức mê mẩn cái TUI ánh xanh mà ai cũng có thể tạo ra
Bản gốc nằm trong Turbo Pascal 6, còn bản port C++ ra đời sau
Vậy nên có thể xem đây là một bản port hiện đại của một bản port
Borland cũng từng như vậy với các framework khác; OWL ban đầu cũng đến từ phía Turbo Pascal for Windows 1.5 trước, và khá nhiều công cụ của C++ Builder thật ra được viết bằng Delphi
Object Pascal của Turbo Pascal 5.5, rồi Turbo Vision của bản 6 là nơi tôi nhập môn OOP, và tôi cảm thấy mình đã may mắn khi đi theo con đường đó
Ngay cả trong môi trường như MS-DOS, tôi vẫn có thể học rất rõ các lợi ích của OOP và kiểu framework mà Turbo Vision mang lại
Khi Borland tung ra Turbo Pascal, Turbo C++, TurboVision, tôi cảm thấy cả một vũ trụ khả năng bỗng mở ra
Hiệu năng compiler cũng tuyệt vời, còn các cuốn manual thì như tác phẩm nghệ thuật; ước gì tôi vẫn còn giữ những cuốn đó
Đây đúng là một báu vật văn hóa
Đầu thập niên 90, tôi gần như tự học C/C++ chỉ bằng cách đọc Turbo C++ và cả chồng sách Borland đi kèm, còn bây giờ thì thật khó tưởng tượng cảnh chỉ đọc tài liệu tham khảo mà học như thế
Các framework TUI mới lúc nào cũng cho cảm giác thiếu thiếu gì đó, và giờ tôi định dùng lại cái này để xem đó có chỉ là hoài niệm hay không
Tôi sẽ đưa nó vào công cụ tiếp theo của mình, và muốn gửi lời tán dương lớn tới những người đã làm ra nó
Trừ GW-BASIC và MS-DOS, còn lại từ Turbo BASIC, Turbo Pascal, Turbo C++ cho MS-DOS và Windows 3.x, đến Turbo Vision, OWL, tất cả đều là Borland
Tôi chỉ dùng VC++ vào khoảng bản 5, và MFC lúc nào cũng thấy quá nhạt nhòa so với sản phẩm của Borland
Ngay cả bây giờ, cũng hiếm thứ nào thực sự bắt kịp năng lực RAD của C++ Builder, và .NET cũng mất khá lâu mới giải quyết được câu chuyện viết mức thấp và AOT kiểu Delphi
Tôi nghĩ nên phát cho các lập trình viên Go, C++, Rust mỗi người vài bản Turbo Pascal 7 cho MS-DOS cùng Delphi hiện đại
Turbo Vision 2.0 đến giờ vẫn khá thực dụng, nên một năm trước tôi đã trực tiếp dùng nó cho một công việc nguyên mẫu
Tôi đã thử làm một frontend Turbo Vision cho debugger LLDB để nó hoạt động giống Turbo Debugger của Borland, và phần lớn mọi thứ diễn ra đúng như mong muốn
Thật đáng ngạc nhiên khi nó như tiếp nối đúng chỗ đã dừng lại từ những năm 199x, và tôi còn có thể compile rồi chạy mã từ năm 1993 mà không gặp vấn đề lớn
Trình editor bên trong cũng có một phiên bản tốt hơn dựa trên Scintilla, với các tính năng như syntax highlighting, nhưng phần tôi định sửa thì không suôn sẻ lắm nên có lẽ phải nhờ tác giả giúp
Tuy vậy, theo nghĩa tri thức chung hiện đại thì tài liệu vẫn còn thiếu, nên khó mà hỏi Stack Overflow hay AI; tôi buộc phải quay về cách cũ là học bằng ví dụ mã nguồn và đọc đi đọc lại vài cuốn sách Turbo Vision
Layout thủ công khá phiền, nên sẽ hay nếu có auto layout kiểu Qt, và tôi cũng hơi nhớ splitter, dù việc tự triển khai có vẻ không khó
Một điều nữa khiến tôi ngạc nhiên là TV thực ra khá nhỏ gọn và compact. Hồi thập niên 90 nó từng cho cảm giác rất đồ sộ
Nhìn chung, công cuộc hiện đại hóa được làm rất tốt và tôi cực kỳ thích nó
Turbo Vision ban đầu thật ra đã có rất nhiều tài liệu chất lượng cao
Trái lại, tôi nghĩ thứ thiếu tài liệu lại là phía hiện đại ngày nay
https://archive.org/details/bitsavers_borlandTurrogrammingGuide1992_25707423
Chỉ cần nhìn thấy cả đống chỉ thị cmake là tôi đã muốn quay lại quá khứ
Với Turbo C hay Pascal, chỉ cần nhấn F9 là chạy ngay
Mặt khác, tôi cũng thấy điều này cho thấy sự bất lực của toolchain của chúng ta
Ở thời đại này, lẽ ra chỉ cần trỏ vào một compiler online để chạy ngay, hoặc tải về rồi mở một thư mục và chạy là xong, nhưng nó đã thành nghi thức hơn là công cụ
./configure && make && make install mới đúng là gold standard và vẫn nên như vậy
Đây chỉ là một trong các bản port/bản clone của Turbo Vision
Phía C++ còn có cái này: https://github.com/kloczek/tvision
Bản đi kèm FreePascal/Lazarus được viết bằng Pascal, và cũng có một bản Rust, dù trông hơi vibe-coded: https://github.com/aovestdipaperino/turbo-vision-4-rust
Chạy nó trong terminal thì có phần mất đi cảm giác cốt lõi mà chuột của màn hình chế độ văn bản mang lại
Trên màn hình text mode thật, nó không hiện như con trỏ chuột mà giống một khối vàng di chuyển bằng chuột hơn
Tôi tò mò không biết có ai từng chạy nó trên text mode Linux độ phân giải cao với GPM chưa
Nó hoạt động bằng cách đảo màu của ô bị đè lên, và vì cửa sổ chính tối xanh lam thường phủ phần lớn màn hình nên kết quả thường trông như một khối vàng sáng
Tôi khuyên nghe tập gần đây của Wookash podcast nói về Chuck Jazdzewski
Ông là một thành viên của đội ngũ gốc tạo ra Turbo Vision, và cũng có nhiều câu chuyện về toàn bộ hệ sinh thái đó
Tôi vẫn muốn Turbo Vision thật sự, tức bản Pascal, hơn là bản C++
Bản C++ cuối cùng vẫn cho cảm giác gần như là phía được chuyển từ bản Pascal sang
Ví dụ trong Pascal,
useslà từ khóa, còn kiểu include module bằng#definethì thế nào cũng thấy như một trò hackDĩ nhiên bây giờ có thể khác biệt đó không còn lớn nữa
IDE text mode cũng dùng Free Vision
https://wiki.lazarus.freepascal.org/images/1/19/Userscreen.png
Tuy nhiên, điểm khác biệt cốt lõi là Free Vision và Turbo Vision dùng kiểu
objecttừ thời Turbo Pascal 5.5 chứ không phảiclasscủa Delphiclassnhờ có RTTI nên dễ hiện thực những thứ như tuần tự hóa tự động, cònobjectthì không có; vì vậy nếu muốn phân biệt các kiểu khác nhau lúc runtime thì phải làm tuần tự hóa thủ công, chẳng hạn đăng ký VMT pointer nằm ở offset cố định của object pointerFree Pascal có bổ sung cho
objectmột số tiện ích như private/protected/public, property, nhưng Free Vision không dùng các phần mở rộng đó vì phải triển khai API Turbo Vision gốc