.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "LIBTTYREC 3" .TH LIBTTYREC 3 "2020-04-19" "0.19" "termrec" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" libttyrec \- a library for handling ttyrec files .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fB#include \fR .PP Link with \fI\-ltty\fR. .SH "DESCRIPTION" .IX Header "DESCRIPTION" .SS "Helper stream function:" .IX Subsection "Helper stream function:" .IP "\fBint open_stream(int \fR\fIfd\fR\fB, const char* \fR\fIurl\fR\fB, int \fR\fImode\fR\fB, const char **\fR\fIerror\fR\fB);\fR" 4 .IX Item "int open_stream(int fd, const char* url, int mode, const char **error);" This function opens a stream designated by the \fIurl\fR. If the given url ends in \*(L".gz\*(R", \*(L".xz\*(R", \*(L".bz2\*(R" or \*(L".zst\*(R"), the stream is assumed to be compressed and gets passed through the appropriate [un]packer. If the \fIfd\fR is not \-1, it is the descriptor you already opened; if it is \-1, the file is opened. The \fImode\fR can be: .RS 4 .IP "\s-1SM_READ\s0" 4 .IX Item "SM_READ" .PD 0 .IP "\s-1SM_WRITE\s0" 4 .IX Item "SM_WRITE" .IP "\s-1SM_REPREAD\s0" 4 .IX Item "SM_REPREAD" .IP "\s-1SM_APPEND\s0" 4 .IX Item "SM_APPEND" .RE .RS 4 .PD .Sp On error, \-1 is returned. If you want the message, pass a non-null pointer \fIerror\fR, it will be filled in. .RE .SS "Format encoders:" .IX Subsection "Format encoders:" .IP "\fBconst char* ttyrec_w_find_format(const char *\fR\fIformat\fR\fB, const char *\fR\fIfilename\fR\fB, const char *\fR\fIfallback\fR\fB);\fR" 4 .IX Item "const char* ttyrec_w_find_format(const char *format, const char *filename, const char *fallback);" This function searches for a format that would fit the \fIfilename\fR provided \*(-- that is, abc.ttyrec.gz returns \*(L"ttyrec\*(R". If no known extension is found, the function returns whatever you gave as \&\fIfallback\fR. You can force a \fIformat\fR yourself, in this case, it is only validated and if invalid, 0 is returned regardless of the other two arguments. .IP "\fBrecorder ttyrec_w_open(int \fR\fIfd\fR\fB, const char *\fR\fIformat\fR\fB, const char *\fR\fIfilename\fR\fB, const struct timeval *\fR\fIts\fR\fB);\fR" 4 .IX Item "recorder ttyrec_w_open(int fd, const char *format, const char *filename, const struct timeval *ts);" A recorder is opened, writing to the file \fIfd\fR; if it's not given (=\-1), the function calls \fB\fBopen_stream()\fB\fR. Recording is done in the given \fIformat\fR, if not provided, the format is guessed based on the \fIfilename\fR. If you provide a timestamp \fIts\fR, it becomes date of the recording. .IP "\fBint ttyrec_w_write(recorder \fR\fIr\fR\fB, const struct timeval *tm, const char *\fR\fIdata\fR\fB, int \fR\fIlen\fR\fB);\fR" 4 .IX Item "int ttyrec_w_write(recorder r, const struct timeval *tm, const char *data, int len);" A chunk of data of length \fIlen\fR is written to recorder \fIr\fR, with timestamp \fItm\fR. Returns 1 if some sort of write error happened. .IP "\fBint ttyrec_w_close(recorder \fR\fIr\fR\fB);\fR" 4 .IX Item "int ttyrec_w_close(recorder r);" All pending data is flushed, recorder closed and its memory freed. Returns 1 if there's some kind of failure. .IP "\fBconst char* ttyrec_w_get_format_name(int \fR\fIi\fR\fB);\fR" 4 .IX Item "const char* ttyrec_w_get_format_name(int i);" You can use this function to enumerate known write formats by calling it with \fIi\fR being subsequent numbers starting at 0. An invalid \fIi\fR will return a null pointer. .IP "\fBconst char* ttyrec_w_get_format_ext(const char *\fR\fIformat\fR\fB);\fR" 4 .IX Item "const char* ttyrec_w_get_format_ext(const char *format);" If the given write \fIformat\fR is associated with a file extension, it is returned. .SS "Format decoders:" .IX Subsection "Format decoders:" .IP "\fBconst char* ttyrec_r_find_format(const char *\fR\fIformat\fR\fB, const char *\fR\fIfilename\fR\fB, const char *\fR\fIfallback\fR\fB);\fR" 4 .IX Item "const char* ttyrec_r_find_format(const char *format, const char *filename, const char *fallback);" See the _w_ function, except that read formats are searched instead. .IP "\fBconst char* ttyrec_r_get_format_name(int \fR\fIi\fR\fB);\fR" 4 .IX Item "const char* ttyrec_r_get_format_name(int i);" ditto .IP "\fBconst char* ttyrec_r_get_format_ext(const char *\fR\fIformat\fR\fB);\fR" 4 .IX Item "const char* ttyrec_r_get_format_ext(const char *format);" ditto .IP "\fBint ttyrec_r_play(int \fR\fIfd\fR\fB, const char *\fR\fIformat\fR\fB, const char *\fR\fIfilename\fR\fB, void (*\fR\fIsynch_init_wait\fR\fB)(const struct timeval *ts, void *\fR\fIarg\fR\fB), void (*\fR\fIsynch_wait\fR\fB)(const struct timeval *\fR\fIdelay\fR\fB, void *\fR\fIarg\fR\fB), void (*\fR\fIsynch_print\fR\fB)(const char *\fR\fIdata\fR\fB, int \fR\fIlen\fR\fB, void *\fR\fIarg\fR\fB), void *\fR\fIarg\fR\fB);\fR" 4 .IX Item "int ttyrec_r_play(int fd, const char *format, const char *filename, void (*synch_init_wait)(const struct timeval *ts, void *arg), void (*synch_wait)(const struct timeval *delay, void *arg), void (*synch_print)(const char *data, int len, void *arg), void *arg);" This function decodes the file \fIfd\fR (opening \fIfilename\fR if \fIfd\fR=\-1). If its contents contain the date of the recording, you'll receive it through the callback \fIsynch_init_wait\fR. Between frames, the delay is passed through \fIsynch_wait\fR. The actual frame data goes through \&\fIsynch_print\fR. Note that in some formats, two or more consecutive delays or two consecutive frames can happen one after another. If you provide an arbitrary \fIarg\fR, it will be passed to all callbacks. .Sp The function doesn't return until the end of input data. Returns 1 on success, 0 on failure. .SS "The following functions deal with in-memory ttyrecs:" .IX Subsection "The following functions deal with in-memory ttyrecs:" .IP "\fBttyrec ttyrec_init(tty \fR\fIvt\fR\fB);\fR" 4 .IX Item "ttyrec ttyrec_init(tty vt);" An empty one is allocated. If a \fIvt\fR is provided, it becomes the screen that the ttyrec is internally played on; otherwise, a blank 80x25 one is allocated. The \fIvt\fR is consumed. .IP "\fBttyrec ttyrec_load(int \fR\fIfd\fR\fB, const char *\fR\fIformat\fR\fB, const char *\fR\fIfilename\fR\fB, tty \fR\fIvt\fR\fB);\fR" 4 .IX Item "ttyrec ttyrec_load(int fd, const char *format, const char *filename, tty vt);" This function will load a ttyrec from the file designated with \fIfd\fR. If it's not open yet (\fIfd\fR=\-1), it will be opened with \fBopen_stream\fR. The ttyrec is played on the \fIvt\fR you provide \&\*(-- or on a new 80x25 one. .IP "\fBvoid ttyrec_free(ttyrec \fR\fItr\fR\fB);\fR" 4 .IX Item "void ttyrec_free(ttyrec tr);" Destroys the in-memory ttyrec, freeing its memory. .PP You can read the ttyrec's data while it's being read; all functions are thread-safe with regard to reading. The frames are stored in structures like this: .PP typedef struct { struct timeval t; int len; char *data; } *ttyrec_frame; .IP "\fBttyrec_frame ttyrec_seek(ttyrec \fR\fItr\fR\fB, const struct timeval *\fR\fIt\fR\fB, tty *\fR\fIvt\fR\fB);\fR" 4 .IX Item "ttyrec_frame ttyrec_seek(ttyrec tr, const struct timeval *t, tty *vt);" Finds the frame that should be shown at time \fI*t\fR, or the first frame if \fIt\fR is null. If \fIvt\fR is not-null, it will receive a terminal containing the screen at that frame. .IP "\fBttyrec_frame ttyrec_next_frame(ttyrec \fR\fItr\fR\fB, ttyrec_frame \fR\fItfv\fR\fB);\fR" 4 .IX Item "ttyrec_frame ttyrec_next_frame(ttyrec tr, ttyrec_frame tfv);" Returns the next frame after frame \fItfv\fR, or null if \fItfv\fR was the last. .IP "\fBvoid ttyrec_add_frame(ttyrec \fR\fItr\fR\fB, const struct timeval *\fR\fIdelay\fR\fB, const char *\fR\fIdata\fR\fB, int \fR\fIlen\fR\fB);\fR" 4 .IX Item "void ttyrec_add_frame(ttyrec tr, const struct timeval *delay, const char *data, int len);" Creates a new frame and appends it to ttyrec \fItr\fR. .IP "\fBint ttyrec_save(ttyrec \fR\fItr\fR\fB, int \fR\fIfd\fR\fB, const char *\fR\fIformat\fR\fB, const char *\fR\fIfilename\fR\fB, const struct timeval *\fR\fIselstart\fR\fB, const struct timeval *\fR\fIselend\fR\fB);\fR" 4 .IX Item "int ttyrec_save(ttyrec tr, int fd, const char *format, const char *filename, const struct timeval *selstart, const struct timeval *selend);" Exports the ttyrec to a new file. If \fIselstart\fR and/or \fIselend\fR are given, they designate the part that should be exported \*(-- if not, the whole is. .SS "\fBstruct timeval\fP arithmetics:" .IX Subsection "struct timeval arithmetics:" A handful of macros for operating on timeval values: .IP "\fBtadd(t, d)\fR" 4 .IX Item "tadd(t, d)" t+=d; .IP "\fBtsub(t, d)\fR" 4 .IX Item "tsub(t, d)" t\-=d; .IP "\fBtmul1000(t, m)\fR" 4 .IX Item "tmul1000(t, m)" t*=m/1000; .IP "\fBtdiv1000(t, m)\fR" 4 .IX Item "tdiv1000(t, m)" t/=m/1000; .IP "\fBtcmp(t1, t2)\fR" 4 .IX Item "tcmp(t1, t2)" If t1t2, +1. 0 otherwise. .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fBlibtty\fR\|(3)