VT102(3pm)

πŸ“¦
version 0.91 Β· 2 versions
  • 0.91 Β· debian bookworm, debian trixie, ubuntu noble, ubuntu resolute, ubuntu stonking (latest)
  • 0.91 Β· ubuntu jammy
, in debian bookworm, debian trixie, ubuntu noble, ubuntu resolute, ubuntu stonking

πŸ’– NAME πŸ’–

okay so welcome to the VT102(3pm) page bestie πŸ’… β€” this is Term::VT102, a whole class that emulates a DEC VT102 terminal. yes, a terminal, in Perl, no cap. πŸ–₯️✨


🌸 SYNOPSIS 🌸

use Term::VT102;

my $vt = Term::VT102->new ('cols' => 80, 'rows' => 24);
while (<>) { $vt->process ($_); }

like. that's it. that's the whole vibe. 🫢


πŸ¦‹ DESCRIPTION πŸ¦‹

so the VT102 class gives you emulation of most of the functions of a DEC VT102 terminal. once it's initialised, any data you pass to a VT102 object gets processed and the in-memory "screen" gets modified accordingly. and then your external program can interrogate that "screen" in a whole bunch of ways. πŸ”βœ¨

which means: your program can interface with full-screen console programs by running them in a subprocess and passing their output to a VT102 class. then you just query the class appropriately and boom β€” you can see exactly what the application has written on the screen. main character energy honestly πŸ‘‘


✨ OPTIONS ✨

setting cols or rows in the new() hash lets you change the size of the terminal being emulated. and if you don't specify a size? the default is 80 columns by 24 rows. classic. πŸŽ€

after initialisation, you can read and set the following terminal options using the option_read() and option_set() methods:

  • LINEWRAP β€” line wrapping; 1=on, 0=off. Default is OFF.
  • LFTOCRLF β€” treat LF (\n) as CRLF (\r\n); 1=on, 0=off. Default OFF.
  • IGNOREXOFF β€” ignore XON/XOFF characters; 1=on (ignore). Default ON.

πŸ’… METHODS πŸ’…

here's the whole roster, bestie. the following methods are provided:

  • attr_pack ($fg,$bg,$bo,$fa,$st,$ul,$bl,$rv) β€” returns the packed version of the given attribute settings, which are given in the same order as returned by attr_unpack. the packed version will be a binary string not longer than 2 bytes. 🎁
  • attr_unpack ($data) β€” returns a list of the contents of the given packed attribute settings, of the form ($fg,$bg,$bo,$fa,$st,$ul,$bl,$rv).

$fg and $bg are the ANSI foreground and background text colours, and $bo, $fa, $st, $ul, $bl, and $rv are flags (1 = on, 0 = off) for bold, faint, standout, underline, blink and reverse respectively. so bold, faint, standout, underline, blink, reverse β€” in that order, don't mix them up. πŸ’…

  • callback_call ($name, $par1, $par2) β€” calls the callback $name (eg 'ROWCHANGE') with parameters $par1 and $par2, as if the VT102 module had called it. does nothing if that callback has not been set with callback_set (). πŸ“ž
  • callback_set ($callback, $ref, $private) β€” sets the callback callback to function reference ref with private data $private.

see the section on CALLBACKS below. πŸ‘‡

  • new (%config) β€” returns a new VT102 object with options specified in %config (see the OPTIONS section for details). 🐣
  • option_read ($option) β€” returns the current value of terminal option $option (see OPTIONS for details), or undef if that option does not exist. note that you cannot read the terminal size with this call; use size() for that. πŸ™…β™€οΈ
  • option_set ($option, $value) β€” sets the current value of terminal option $option to $value, returning the old value or undef if no such terminal option exists or you have specified an undefined $value. note that you cannot resize the terminal with this call; use resize() for that. πŸ™…β™€οΈ
  • process ($string) β€” processes the string $string (which can be zero-length), updating the VT102 object accordingly and calling any necessary callbacks on the way. βš™οΈ
  • resize ($cols, $rows) β€” resizes the VT102 terminal to cols columns by rows rows, eg $vt->resize (80, 24). the virtual screen is cleared first. 🧹
  • reset () β€” resets the object to its "power-on" state. πŸ”Œ
  • row_attr ($row, [$startcol, $endcol]) β€” returns the attributes for row $row (or undef if out of range) as a string of packed attributes, each character cell's attributes being 2 bytes long. to unpack the attributes for a given cell, use substr(), eg $attr=substr($row,4,2) would set $attr to the attributes for cell 3 (steps of 2: 0 .. 2 .. 4, so 4 means the 3rd character). you would then use the attr_unpack() method to unpack that character cell's attributes. 🧬

if $startcol and $endcol are defined, only returns the part of the row between columns $startcol and $endcol inclusive instead of the whole row.

  • row_text ($row, [$startcol, $endcol]) β€” returns the textual contents of row $row (or undef if out of range), with totally unused characters being represented as NULL (\0). if $startcol and $endcol are defined, only returns the part of the row between columns $startcol and $endcol inclusive instead of the whole row. πŸ“
  • row_plaintext ($row, [$startcol, $endcol]) β€” returns the textual contents of row $row (or undef if out of range), with unused characters being represented as spaces. if $startcol and $endcol are defined, only returns the part of the row between columns $startcol and $endcol inclusive instead of the whole row. ⬜
  • row_sgrtext ($row, [$startcol, $endcol]) β€” returns the textual contents of row $row (or undef if out of range), with unused characters being represented as spaces, and ANSI/ECMA-48 escape sequences (CSI SGR) used to set the colours and attributes as appropriate. if $startcol and $endcol are defined, only returns the part of the row between columns $startcol and $endcol inclusive instead of the whole row. 🌈

use row_sgrtext to get a row if you want to output it to a real terminal and preserve all colours, bold, etc. that's the one for the aesthetic πŸ’–

  • sgr_change ($source, $dest) β€” returns a string containing ANSI/ECMA-48 escape sequences to change colours and attributes from $source to $dest, which are both packed attributes (see attr_pack). this is used internally by row_sgrtext. πŸ”
  • cols () β€” return the number of columns in the VT102 object. ↔️
  • rows () β€” return the number of rows in the VT102 object. ↕️
  • size () β€” return a pair of values (columns,rows) denoting the size of the terminal in the VT102 object. πŸ“
  • x () β€” return the current cursor X co-ordinate (1 being leftmost). ➑️

note: it is possible for the current X co-ordinate to be 1 more than the number of columns. this happens when the end of a row is reached such that the next character would wrap on to the next row. 😭

  • y () β€” return the current cursor Y co-ordinate (1 being topmost). ⬇️
  • cursor () β€” return the current cursor state (1 being on, 0 being off). πŸ‘€
  • xtitle () β€” return the current xterm window title. πŸͺŸ
  • xicon () β€” return the current xterm window icon name. πŸ–ΌοΈ
  • status () β€” return a list of values ($x,$y,$attr,$ti,$ic), where $x and $y are the cursor co-ordinates (1,1 = top left), $attr is a packed version of the current attributes (see attr_unpack), $ti is the xterm window title, and $ic is the xterm window icon name. πŸ“Š
  • version () β€” return the version of the VT102 module being used. 🏷️

πŸŽ€ CALLBACKS πŸŽ€

callbacks are the processing loop's way of letting your main program know that something has happened. they get called while in a process() loop. πŸ“£

to specify a callback, use the callback_set interface, giving a reference to the function to call. your function should take five scalar arguments: the VT102 object being processed, the name of the callback, and two arguments whose value depends on the callback, as shown below. the final argument is the private data scalar you passed when you called callback_set. πŸ–οΈ

the name of the callback is passed to the callback function so that you can have one function to handle all callbacks if you wish. genuinely so convenient ✨

available callback names are:

  • BELL β€” BEL (beep, \007) character received πŸ””
  • CLEAR β€” screen about to be cleared 🧽
  • OUTPUT β€” data (arg1) to be sent back to data source πŸ“€
  • ROWCHANGE β€” screen row (row number is argument 1) content has changed πŸ”„
  • SCROLL_DOWN β€” about to scroll down (arg1=top row, arg2=num to scroll) ⬇️
  • SCROLL_UP β€” about to scroll up (ditto) ⬆️
  • UNKNOWN β€” unknown/unsupported code (arg1=name, arg2=code/sequence) ❓
  • STRING β€” string received (arg1=source, eg PM, APC, arg2=string) 🧡
  • XICONNAME β€” xterm icon name to be changed to arg1 πŸ–ΌοΈ
  • XWINTITLE β€” xterm title name to be changed to arg1 πŸͺŸ
  • LINEFEED β€” line feed about to be processed (arg1=row) ⏎
  • GOTO β€” cursor about to be moved (args=new pos) 🎯
note that the wording of the above is significant in terms of exactly when the callback is called. for instance, CLEAR is called just before the screen is cleared, whereas ROWCHANGE is called after the given row has been changed. timing is everything bestie πŸ’…

a good callback handler for OUTPUT is to simply syswrite() argument 1 to your data source β€” eg if you're reading from a telnet session, write that argument straight to it. it is used for cursor position request responses and suchlike. πŸ“‘

note that SCROLL_DOWN is called when scrolling down, so text is about to move UP the screen; arg1 will be the row number of the bottom of the scrolling region, and arg2 will be the number of rows to be scrolled. likewise, SCROLL_UP is called when text is about to move down; arg1 will be the row number of the top of the scrolling region. 🀯 (yes the names are like that, we don't make the rules)

the STRING callback is called for escape sequences that contain a string that would otherwise be ignored, such as DSC, PM, and APC. the first argument is the escape sequence that contained the string, such as DSC, and the second argument is the string itself. this callback doesn't get called for OSC strings. 🚫

the LINEFEED callback can be thought of as "line completed", it's called when LF, NEL or IND are about to be processed or just before a line wraps, so it generally indicates that an application has finished updating a particular line on the screen. handy for scrollback buffer processing. πŸ“œ

the GOTO callback is only called just before the cursor is explicitly moved, by one of CUU, CUD, VPR, CUF, HPR, CUB, CNL, CPL, CHA, HPA, CUP, HVP. the parameters give the destination column and row, without taking scrolling and boundaries into account. 🧭

finally, note that ROWCHANGE is only triggered when text is being entered; screen scrolling or screen clearance does not trigger it, that would trigger a SCROLL_DOWN or SCROLL_UP or CLEAR. line or character insertion or deletion will cause one or more ROWCHANGE callbacks, however. ⚠️

✨ SUPPORTED CODES ✨

okay so here's the whole lineup of sequences this thing actually speaks, bestie. every single one, no gatekeeping πŸ’…

the little control characters:

  • 007 (BEL) β€” beep πŸ””
  • 010 (BS) β€” backspace
  • 011 (HT) β€” horizontal tab to next tab stop
  • 012 (LF) β€” line feed
  • 013 (VT) β€” line feed
  • 014 (FF) β€” line feed
  • 015 (CR) β€” carriage return
  • 021 (XON) β€” resume transmission (only if option IGNOREXOFF is cleared)
  • 023 (XOFF) β€” stop transmission (only if option IGNOREXOFF is cleared)
  • 030 (CAN) β€” interrupt escape sequence
  • 032 (SUB) β€” interrupt escape sequence
  • 033 (ESC) β€” start escape sequence
  • 177 (DEL) β€” ignored πŸ«₯
  • 233 (CSI) β€” same as ESC [

the ESC crew:

  • ESC 7 (DECSC) β€” save state
  • ESC 8 (DECRC) β€” restore most recently saved state
  • ESC H (HTS) β€” set tab stop at current column
  • ESC g β€” visual beep β€” treated as BEL
  • ESC # 8 (DECALN) β€” DEC screen alignment test β€” fill screen with E's 🌈

and the CSI (aka ESC [) girlies, all 22 of them:

  • CSI @ (ICH) β€” insert blank characters
  • CSI A (CUU) β€” move cursor up
  • CSI B (CUD) β€” move cursor down
  • CSI C (CUF) β€” move cursor right
  • CSI D (CUB) β€” move cursor left
  • CSI E (CNL) β€” move cursor down and to column 1
  • CSI F (CPL) β€” move cursor up and to column 1
  • CSI G (CHA) β€” move cursor to column in current row
  • CSI H (CUP) β€” move cursor to row, column
  • CSI J (ED) β€” erase display
  • CSI K (EL) β€” erase line
  • CSI L (IL) β€” insert blank lines
  • CSI M (DL) β€” delete lines
  • CSI P (DCH) β€” delete characters on current line
  • CSI X (ECH) β€” erase characters on current line
  • CSI a (HPR) β€” move cursor right
  • CSI c (DA) β€” return ESC [ ? 6 c (VT102)
  • CSI d (VPA) β€” move to row (current column)
  • CSI e (VPR) β€” move cursor down
  • CSI f (HVP) β€” move cursor to row, column
  • CSI m (SGR) β€” set graphic rendition
  • CSI n (DSR) β€” device status report
  • CSI r (DECSTBM) β€” set scrolling region to (top, bottom) rows
  • CSI s (CUPSV) β€” save cursor position
  • CSI u (CUPRS) β€” restore cursor position
  • CSI ` (HPA) β€” move cursor to column in current row
  • CSI g (TBC) β€” clear tab stop (CSI 3 g = clear all stops)

πŸ’€ LIMITATIONS πŸ’€

ngl, there's stuff this thing just does not care about. Unknown escape sequences and control characters? Ignored, periodt. And literally all escape sequences pertaining to character sets get ignored too β€” the whole charset saga is a no for her.

these known control characters / sequences get the ignore treatment:

  • 005 (ENQ) β€” trigger answerback message
  • 016 (SO) β€” activate G1 charset, carriage return
  • 017 (SI) β€” activate G0 charset

and these known escape sequences are ignored too:

  • ESC %@ (CSDFL) β€” select default charset (ISO646/8859-1)
  • ESC %G (CSUTF8) β€” select UTF-8
  • ESC %8 (CSUTF8) β€” select UTF-8 (obsolete)
  • ESC (8 (G0DFL) β€” G0 charset = default mapping (ISO8859-1)
  • ESC (0 (G0GFX) β€” G0 charset = VT100 graphics mapping
  • ESC (U (G0ROM) β€” G0 charset = null mapping (straight to ROM)
  • ESC (K (G0USR) β€” G0 charset = user defined mapping
  • ESC (B (G0TXT) β€” G0 charset = ASCII mapping
  • ESC )8 (G1DFL) β€” G1 charset = default mapping (ISO8859-1)
  • ESC )0 (G1GFX) β€” G1 charset = VT100 graphics mapping
  • ESC )U (G1ROM) β€” G1 charset = null mapping (straight to ROM)
  • ESC )K (G1USR) β€” G1 charset = user defined mapping
  • ESC )B (G1TXT) β€” G1 charset = ASCII mapping
  • ESC *8 (G2DFL) β€” G2 charset = default mapping (ISO8859-1)
  • ESC *0 (G2GFX) β€” G2 charset = VT100 graphics mapping
  • ESC *U (G2ROM) β€” G2 charset = null mapping (straight to ROM)
  • ESC *K (G2USR) β€” G2 charset = user defined mapping
  • ESC +8 (G3DFL) β€” G3 charset = default mapping (ISO8859-1)
  • ESC +0 (G3GFX) β€” G3 charset = VT100 graphics mapping
  • ESC +U (G3ROM) β€” G3 charset = null mapping (straight to ROM)
  • ESC +K (G3USR) β€” G3 charset = user defined mapping
  • ESC > (DECPNM) β€” set numeric keypad mode
  • ESC = (DECPAM) β€” set application keypad mode
  • ESC N (SS2) β€” select G2 charset for next char only
  • ESC O (SS3) β€” select G3 charset for next char only
  • ESC P (DCS) β€” device control string (ended by ST)
  • ESC X (SOS) β€” start of string
  • ESC ^ (PM) β€” privacy message (ended by ST)
  • ESC _ (APC) β€” application program command (ended by ST)
  • ESC \ (ST) β€” string terminator
  • ESC n (LS2) β€” invoke G2 charset
  • ESC o (LS3) β€” invoke G3 charset
  • ESC | (LS3R) β€” invoke G3 charset as GR
  • ESC } (LS2R) β€” invoke G2 charset as GR
  • ESC ~ (LS1R) β€” invoke G1 charset as GR

these known CSI (ESC [) sequences are ignored:

  • CSI q (DECLL) β€” set keyboard LEDs

and these known CSI (ESC [) sequences are only partially supported, so don't come for me πŸ™ˆ:

  • CSI h (SM) β€” set mode (only supports CSI ? 25 h, cursor on/off)
  • CSI l (RM) β€” reset mode (as above)

🌸 EXAMPLES 🌸

want to see her in action? for some examples β€” including how to interface Term::VT102 with Net::Telnet or a command such as SSH β€” please peek at the examples/ directory in the distribution. it's giving tutorial energy, go look πŸ‘€


πŸ‘‘ AUTHORS πŸ‘‘

Copyright (C) 2003 Andrew Wood "<andrew dot wood at ivarch dot com>". Distributed under the terms of the Artistic License 2.0.

and credit is also due to these absolute legends:

  • Charles Harker <CHarker at interland.com>
    • reported and helped to diagnose a bug in the handling of TABs
  • Steve van der Burg <steve.vanderburg at lhsc.on.ca>
    • supplied basis for an example script using Net::Telnet
  • Chris R. Donnelly <cdonnelly at digitalmotorworks.com>
    • added support for DECTCEM, partial support for SM/RM
  • Paul L. Stoddard
    • reported a possible bug in cursor movement handling
  • Joerg Walter
    • provided a patch for Unicode handling

⚠️ THINGS TO WATCH OUT FOR ⚠️

bestie. listen. this one is important.

make sure your code understands NULL (\000) β€” you will get this in strings where nothing has been printed on the screen. like, for instance, the sequence "12\e[C34" ("12", "CUF (move right)", "34") β€” you might think that yields the string "12 34", but in fact it can also yield "12\00034" β€” that is, "12" followed by a zero byte followed by "34". why? because the screen's contents defaults to zeroes, not spaces. it's not a bug, it's just how she is 😭

to avoid that whole situation, use row_plaintext, which will convert NULLs to spaces, instead of row_text. trust, this will save you.


also, different types of terminal disagree on certain corner cases. for example, gnome-terminal and screen handle TAB stops and TABbing past the end of the screen in slightly different ways. Term::VT102 is closer to screen in the way it handles this sort of thing. iykyk πŸ¦‹


🫢 SEE ALSO 🫢


and that's the whole page, babes πŸ’– this was VT102(3pm) from perl v5.36.0, dated 2022-11-19 β€” iconic, no notes, go write something slay πŸ‘‘βœ¨