/* 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 */
/* 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 -------------------------------------------------*/