X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/mLib/blobdiff_plain/e82eb4b16c632551930ca8bfb2b4e2e58c1ee16a..9a5e5808ff09935b8562d7bf93b3ada040d96669:/ui/tty.h diff --git a/ui/tty.h b/ui/tty.h index e026c15..57ed489 100644 --- a/ui/tty.h +++ b/ui/tty.h @@ -162,7 +162,7 @@ enum { /* Other capabilities. */ #define TTCF_RELMV 0x00000100u /* relative cursor motion */ #define TTCF_ABSMV 0x00000200u /* absolute cursor motion */ -#define TTCF_MIXMV 0x00000400u /* mixed cursor motion */ +#define TTCF_MIXMV 0x00000400u /* mixed (y-abs, x-rel) motion */ #define TTCF_MMARG 0x00000800u /* proper magic margins */ #define TTCF_BGER 0x00001000u /* erasure uses background colour */ #define TTCF_ERCH 0x00002000u /* erase characters */ @@ -231,135 +231,380 @@ struct tty_state { /* Terminal control state. */ struct tty { - const struct tty_ops *ops; - FILE *fpin, *fpout; - unsigned baud, ht, wd, f; -#define TTF_BORROW 1u - uint32 acaps, ocaps; - struct tty_state st; + const struct tty_ops *ops; /* operations table (opaque) */ + FILE *fpin, *fpout; /* input and output streams */ + unsigned baud; /* (output) baud rate (bits/s) */ + unsigned ht, wd; /* terminal dimensions */ + unsigned f; /* flags */ +#define TTF_BORROW 1u /* don't close the streams */ + uint32 acaps, ocaps; /* attribute and other capabilities */ + struct tty_state st; /* current terminal state */ }; -struct tty_ops { - void (*release)(struct tty */*tty*/); - int (*setattr)(struct tty */*tty*/, - const struct gprintf_ops */*gops*/, void */*go*/, - const struct tty_attr */*a*/); - int (*setmodes)(struct tty */*tty*/, - const struct gprintf_ops */*gops*/, void */*go*/, - uint32 /*modes_bic*/, uint32 /*modes_xor*/); - int (*move)(struct tty */*tty*/, - const struct gprintf_ops */*gops*/, void */*go*/, - unsigned /*orig*/, int /*y*/, int /*x*/); - int (*repeat)(struct tty */*tty*/, - const struct gprintf_ops */*gops*/, void */*go*/, - int /*ch*/, unsigned /*n*/); - int (*erase)(struct tty */*tty*/, - const struct gprintf_ops */*gops*/, void */*go*/, - unsigned /*f*/); - int (*erch)(struct tty */*tty*/, - const struct gprintf_ops */*gops*/, void */*go*/, - unsigned /*n*/); - int (*ins)(struct tty */*tty*/, - const struct gprintf_ops */*gops*/, void */*go*/, - unsigned /*f*/, unsigned /*n*/); - int (*inch)(struct tty */*tty*/, - const struct gprintf_ops */*gops*/, void */*go*/, - int /*ch*/); - int (*del)(struct tty */*tty*/, - const struct gprintf_ops */*gops*/, void */*go*/, - unsigned /*f*/, unsigned /*n*/); - int (*_res0)(void), (*_res1)(void), (*_res2)(void), (*_res3)(void); -}; +/*----- Lifecycle and maintenance -----------------------------------------*/ + +/* --- @tty_open@ --- * + * + * Arguments: @FILE *fp@ = stream open on terminal, or null + * @unsigned f@ = flags (@TTF_...@) + * @const unsigned *backends@ = ordered list of backends to try + * + * Returns: Pointer to terminal control block, or null on error. + * + * Use: Open a terminal and return a @struct tty *@ terminal control + * block pointer. + * + * If @fp@ is provided, then it will be used for terminal + * output. If @fp@ is @stdout@, then input (not currently + * supported) will come from @stdin@; otherwise, input comes + * from @fp@. If @fp@ is null and @TTF_OPEN@ is set, then + * @tty_open@ will attempt to open a terminal for itself: if + * @stdin@ is interactive then it used for input; if @stdout@ -- + * or, failing that, @stderr@ -- is interactive, then it is used + * for output; otherwise %|/dev/tty|% is opened and used. If + * @fp@ is null and @TTF_OPEN@ is not set, then a usable + * terminal control block is still returned, but output cannot + * be sent directly to the terminal -- since there isn't one. + * + * If @TTF_BORROW@ is set, then the stream will not be closed by + * @tty_close@. (This flag is ignored if @fp@ is null.) + * + * If @beckends@ is provided, then it points to a vector of + * @TTBK_...@ constants describing the backends to be tried in + * order. The vector is terminated by @TTBK_END@; if this is + * found, then a null pointer is returned. + * + * A null control block pointer is valid for all @tty@ + * functions: most will just immediately report failure, but + * there won't be any crashing. + */ -/*----- Functions provided ------------------------------------------------*/ +#define TTF_OPEN 256u /* open terminal if @fp@ is null */ +extern struct tty *tty_open(FILE */*fp*/, unsigned /*f*/, + const unsigned */*backends*/); -/* --- @tty_clampattr@ --- * +/* --- @tty_close@ --- * * - * Arguments: @struct tty_attr *a_out@ = selected attributes - * @const struct tty_attr *a@ = requested attributes - * @uint32 acaps@ = terminal's attribute capability mask + * Arguments: @struct tty *tty@ = control block pointer * * Returns: --- * - * Use: Select the closest approximation to the requested attributes - * which can be accommodated by the terminal. + * Use: Closes a terminal, releasing the control block and any + * resources it held. In particular, if the terminal was opened + * without @TTF_BORROW@, then the output stream is closed. */ -extern void tty_clampattr(struct tty_attr */*a_out*/, - const struct tty_attr */*a*/, uint32 /*acaps*/); - +extern void tty_close(struct tty */*tty*/); +/* --- @tty_resized@ --- * + * + * Arguments: @struct tty *tty@ = control block pointer + * + * Returns: Zero if the size hasn't changed, %$+1$% if the size has + * changed, or %$-1$% on error. + * + * Use: Update the terminal width and height. Call this after + * receiving @SIGWINCH@, or otherwise periodically, to avoid + * making a mess. + */ extern int tty_resized(struct tty */*tty*/); -#define TTF_OPEN 256u -extern struct tty *tty_open(FILE */*fp*/, unsigned /*f*/, - const unsigned */*backends*/); +/*----- Output and control functions --------------------------------------*/ -extern void tty_close(struct tty */*tty*/); +/* These functions come in pairs. The unmarked version sends output directly + * to the terminal's output stream, and will obviously fail (though not + * crash) if this is null; the version whose names ends with @...g@ accepts + * an additional pair of arguments @gops@ and @go@ giving an alternative + * destination for output. + */ + +/* --- @tty_setattr@, @tty_setattrg@--- * + * + * Arguments: @struct tty *tty@ = control block pointer + * @const struct gprintf_ops *gops, void *go@ = output + * destination + * @const struct tty_attr *a@ = pointer to attributes to set, or + * null + * + * Returns: Zero on success, %$-1$% on error. + * + * Use: Set the indicated formatting attributes on following output. + * If @a@ is null, then clear all attributes, just as if + * @a->f == 0@. + * + * If you only ever request attributes which are advertised in + * the terminals capapbility masked, then you'll always get what + * you asked for. Otherwise, the provided attributes will be + * `clamped', i.e., modified so as to accommodate the terminal's + * shortcomings. In simple cases, unsupported attributes may + * just be dropped; but they can also be substituted, e.g., + * single underlining for double, or approximate colours for + * unsupported colours. + */ +extern int tty_setattr(struct tty */*tty*/, const struct tty_attr */*a*/); extern int tty_setattrg(struct tty */*tty*/, const struct gprintf_ops */*gops*/, void */*go*/, const struct tty_attr */*a*/); -extern int tty_setattr(struct tty */*tty*/, const struct tty_attr */*a*/); +/* --- @tty_setattrlist@, @tty_setattrlistg@ --- * + * + * Arguments: @struct tty *tty@ = control block pointer + * @const struct gprintf_ops *gops, void *go@ = output + * destination + * @const struct tty_attrlist *aa@ = pointer to attribute list + * `menu' + * + * Returns: Zero on success, %$-1$% on error. + * + * Use: Search the list for an entry matching the terminal's + * capabilities, i.e., @tty->acaps&a->cap_mask == a->cap_eq@. + * The attributes in the first such entry are set, as if by + * @tty_setattr@. + * + * The list is terminated by an entry with @cap_mask == 0@ -- + * though it will be checked like any other before ending the + * search. In particular, this means that an entry with + * @cap_mask == cap_eq == 0@ is a `catch-all', and its + * attributes will be set if no earlier matching entry could be + * found, while an entry with @cap_mask == 0@ and @cap_eq != 0@ + * terminates the search without setting any attributes. + */ +extern int tty_setattrlist(struct tty */*tty*/, + const struct tty_attrlist */*aa*/); extern int tty_setattrlistg(struct tty */*tty*/, const struct gprintf_ops */*gops*/, void */*go*/, const struct tty_attrlist */*aa*/); -extern int tty_setattrlist(struct tty */*tty*/, - const struct tty_attrlist */*aa*/); +/* --- @tty_setmodes@, @tty_setmodesg@ --- * + * + * Arguments: @struct tty *tty@ = control block pointer + * @const struct gprintf_ops *gops, void *go@ = output + * destination + * @uint32 modes_bic, modes_xor@ = masks to apply to the modes + * settings + * + * Returns: Zero on success, %$-1$% on error. + * + * Use: Adjust the terminal display modes: specifically, the modes + * are adjusted to be @(modes&~modes_bic) ^ modes_xor@. + * Mode bits which aren't supported by the terminal are + * ignored. + */ +extern int tty_setmodes(struct tty */*tty*/, + uint32 /*modes_bic*/, uint32 /*modes_xor*/); extern int tty_setmodesg(struct tty */*tty*/, const struct gprintf_ops */*gops*/, void */*go*/, uint32 /*modes_bic*/, uint32 /*modes_xor*/); -extern int tty_setmodes(struct tty */*tty*/, - uint32 /*modes_bic*/, uint32 /*modes_xor*/); +/* --- @tty_move@, @tty_moveg@ --- * + * + * Arguments: @struct tty *tty@ = control block pointer + * @const struct gprintf_ops *gops, void *go@ = output + * destination + * @unsigned orig@ = origin + * @int y, x@ = new cursor position + * + * Returns: Zero on success, %$-1$% on error. + * + * Use: Move the cursor. Coordinates are numbered starting with 0. + * + * The @y@ position is interpreted relative to the origin given + * by @orig@: if @TTOF_YCUR@ is set, then the motion is relative + * to the current cursor row; otherwise, it is relative to + * the top `home' row. Similarly, the @x@ position is + * interpreted relative to the current cursor column if + * @TTOF_XCUR is set, otherwise relative to the leftmost + * column. + * + * Not all terminals are capable of all kinds of motions: + * @TTCF_ABSMV@ is set if absolute motion is possible, and + * @TTCF_RELMV@ is set if relative motion is possible. The + * @TTCF_MIXMV@ bit indicates that the combination of absolute-y + * and relative-x motion is possible; note that the combination + * of relative-y and absolute-x is always possible if relative + * motion is possible at all. + * + * The above notwithstanding, all terminals are assumed capable + * of moving the cursor to the start of either the current line + * @tty_move(tty, TTOF_YCUR | TTOF_XHOME, 0, 0)@, or of the next + * line @tty_move(tty, TTOF_YCUR | TTOF_XHOME, +1, 0)@. + */ +extern int tty_move(struct tty */*tty*/, + unsigned /*orig*/, int /*y*/, int /*x*/); extern int tty_moveg(struct tty */*tty*/, const struct gprintf_ops */*gops*/, void */*go*/, unsigned /*orig*/, int /*y*/, int /*x*/); -extern int tty_move(struct tty */*tty*/, - unsigned /*orig*/, int /*y*/, int /*x*/); +/* --- @tty_repeat@, @tty_repeatg@ --- * + * + * Arguments: @struct tty *tty@ = control block pointer + * @const struct gprintf_ops *gops, void *go@ = output + * destination + * @int ch@ = character to write + * @unsigned n@ = number of copies + * + * Returns: Zero on success, %$-1$% on error. + * + * Use: Write @n@ copies of the character @ch@ to the terminal. + * (Some terminals have a special control sequence for doing + * this.) + */ +extern int tty_repeat(struct tty */*tty*/, int /*ch*/, unsigned /*n*/); extern int tty_repeatg(struct tty */*tty*/, const struct gprintf_ops */*gops*/, void */*go*/, int /*ch*/, unsigned /*n*/); -extern int tty_repeat(struct tty */*tty*/, int /*ch*/, unsigned /*n*/); +/* --- @tty_erase@, @tty_eraseg@ --- * + * + * Arguments: @struct tty *tty@ = control block pointer + * @const struct gprintf_ops *gops, void *go@ = output + * destination + * @unsigned f@ = flags + * + * Returns: Zero on success, %$-1$% on error. + * + * Use: Erase portions of the current line or the whole display. + * + * If @TTEF_DSP@ is set, then the whole display is affected. If + * @TTEF_BEGIN@ is set, then the display is erased starting from + * the top left and ending at and including the cursor + * position. If @TTEF_END@ is set, then the display is erased + * starting from and including the cursor position, and ending + * at the bottom right. If both flags are set, then, + * additionally, the cursor is moved to its `home' position at + * the top left. + * + * If @TTF_DSP@ is not set, then the current line is affected. + * If @TTEF_BEGIN@ is set, then the line is erased starting from + * the left and ending at and including the cursor position. If + * @TTEF_END@ is set, then the line is erased starting from and + * including the cursor position, and ending at the right hand + * side. + * + * If the @TTCF_BGER@ capability is set, then the erased + * positions take on the current background colour; otherwise, + * they have the default background colour. + */ +extern int tty_erase(struct tty */*tty*/, unsigned /*f*/); extern int tty_eraseg(struct tty */*tty*/, const struct gprintf_ops */*gops*/, void */*go*/, unsigned /*f*/); -extern int tty_erase(struct tty */*tty*/, unsigned /*f*/); +/* --- @tty_erch@, @tty_erchg@ --- * + * + * Arguments: @struct tty *tty@ = control block pointer + * @const struct gprintf_ops *gops, void *go@ = output + * destination + * @unsigned n@ = number of characters to erase + * + * Returns: Zero on success, %$-1$% on error. + * + * Use: Erase a number of characters, starting from and including the + * current cursor position. + * + * If the @TTCF_BGER@ capability is set, then the erased + * positions take on the current background colour; otherwise, + * they have the default background colour. + */ +extern int tty_erch(struct tty */*tty*/, unsigned /*n*/); extern int tty_erchg(struct tty */*tty*/, const struct gprintf_ops */*gops*/, void */*go*/, unsigned /*n*/); -extern int tty_erch(struct tty */*tty*/, unsigned /*n*/); +/* --- @tty_ins@, @tty_insg@ --- * + * + * Arguments: @struct tty *tty@ = control block pointer + * @const struct gprintf_ops *gops, void *go@ = output + * destination + * @unsigned f@ = flags + * @unsigned n@ = number of items to insert + * + * Returns: Zero on success, %$-1$% on error. + * + * Use: Insert a number of blank characters or lines. + * + * If @TTIDF_LN@ is set, then insert @n@ blank lines above the + * current line. The cursor must be at the far left of the + * line. + * + * Otherwise, insert @n@ empty character spaces at the cursor + * position. + */ +extern int tty_ins(struct tty */*tty*/, unsigned /*f*/, unsigned /*n*/); extern int tty_insg(struct tty */*tty*/, const struct gprintf_ops */*gops*/, void */*go*/, unsigned /*f*/, unsigned /*n*/); -extern int tty_ins(struct tty */*tty*/, unsigned /*f*/, unsigned /*n*/); +/* --- @tty_inch@ --- * + * + * Arguments: @struct tty *tty@ = control block pointer + * @const struct gprintf_ops *gops, void *go@ = output + * destination + * @int ch@ = character to insert + * + * Returns: Zero on success, %$-1$% on error. + * + * Use: Insert a single character. + * + * If the @TTMF_INS@ mode is advertised, then insert mode must + * be set before calling this function. + */ +extern int tty_inch(struct tty */*tty*/, int /*ch*/); extern int tty_inchg(struct tty */*tty*/, const struct gprintf_ops */*gops*/, void */*go*/, int /*ch*/); -extern int tty_inch(struct tty */*tty*/, int /*ch*/); +/* --- @tty_del@, @tty_delg@ --- * + * + * Arguments: @struct tty *tty@ = control block pointer + * @const struct gprintf_ops *gops, void *go@ = output + * destination + * @unsigned f@ = flags + * @unsigned n@ = number of items to delete + * + * Returns: Zero on success, %$-1$% on error. + * + * Use: Delete a number of characters or lines. + * + * If @TTIDF_LN@ is set, then delete @n@ blank lines, starting + * with the current line line. The cursor must be at the far + * left of the line. + * + * Otherwise, delete @n@ characters at the cursor position. + */ +extern int tty_del(struct tty */*tty*/, unsigned /*f*/, unsigned /*n*/); extern int tty_delg(struct tty */*tty*/, const struct gprintf_ops */*gops*/, void */*go*/, unsigned /*f*/, unsigned /*n*/); -extern int tty_del(struct tty */*tty*/, unsigned /*f*/, unsigned /*n*/); +/* --- @tty_restore@, @tty_restoreg@ --- * + * + * Arguments: @struct tty *tty@ = control block pointer + * @const struct gprintf_ops *gops, void *go@ = output + * destination + * @const struct tty_state *st@ = state to restore + * + * Returns: Zero on success, %$-1$% on error. + * + * Use: Restore the terminal modes and attributes to match a + * state previously captured by copying @tty->st@. + */ + +extern int tty_restore(struct tty */*tty*/, const struct tty_state */*st*/); +extern int tty_restoreg(struct tty */*tty*/, + const struct gprintf_ops */*gops*/, void */*go*/, + const struct tty_state */*st*/); /*----- That's all, folks -------------------------------------------------*/