term: extend tty interface with more features

This commit is contained in:
2024-11-20 22:12:36 +00:00
parent 7e440f213c
commit dafa74b1b9
13 changed files with 3163 additions and 317 deletions

View File

@@ -1,5 +1,7 @@
#include <blue/term.h> #include <blue/term/tty.h>
#include <blue/term/print.h>
#include <blue/object/string.h> #include <blue/object/string.h>
#include <stdio.h>
#define F_GREEN "[green]" #define F_GREEN "[green]"
#define F_YELLOW "[yellow]" #define F_YELLOW "[yellow]"
@@ -52,9 +54,9 @@ static const char *text2
int main(void) int main(void)
{ {
const char *s = "[magenta,uline]Hello, [bright]world![reset]"; const char *s = "[magenta,uline]Hello, [bright_magenta]world![reset]";
b_fputs(s, stdout); b_puts(s);
fputc('\n', stdout); b_putc('\n');
b_string *str = b_string_create_from_cstr(s); b_string *str = b_string_create_from_cstr(s);
size_t len = b_string_get_size(str, B_STRLEN_IGNORE_MOD); size_t len = b_string_get_size(str, B_STRLEN_IGNORE_MOD);
@@ -65,11 +67,13 @@ int main(void)
format.p_right_margin = 5; format.p_right_margin = 5;
format.p_flags = B_PARAGRAPH_DOUBLE_LINE_BREAK; format.p_flags = B_PARAGRAPH_DOUBLE_LINE_BREAK;
b_print_paragraph(text, stdout, &format); b_print_paragraph(text, b_stdtty, &format);
b_i("An informational message\n\nWith multiple lines"); b_i("An informational message\n\nWith multiple lines");
b_warn("A warning message\nWith multiple lines"); b_warn("A warning message\nWith multiple lines");
b_err("An error message\nWith multiple lines"); b_err("An error message\nWith multiple lines");
b_printf("[red]formatting ignored: '%s'[reset]\n[dark_grey]dark text[reset]\n", "[blue]wow![reset]");
return 0; return 0;
} }

View File

@@ -1,43 +1,10 @@
#ifndef BLUELIB_PRINT_H_ #ifndef BLUELIB_TERM_H_
#define BLUELIB_PRINT_H_ #define BLUELIB_TERM_H_
#include <blue/core/status.h> #include <blue/core/status.h>
#include <stdio.h> #include <stdio.h>
#define b_i(...) b_print(B_PRINT_I, __VA_ARGS__) #include <blue/term/tty.h>
#define b_warn(...) b_print(B_PRINT_WARN, __VA_ARGS__) #include <blue/term/print.h>
#define b_err(...) b_print(B_PRINT_ERR, __VA_ARGS__)
typedef enum b_paragraph_format_flags {
B_PARAGRAPH_DONT_INDENT_FIRST_LINE = 0x01u,
B_PARAGRAPH_HYPHENATE = 0x02u,
B_PARAGRAPH_DOUBLE_LINE_BREAK = 0x04u,
B_PARAGRAPH_MULTI_LINE_BREAK = 0x08u,
} b_paragraph_format_flags;
typedef struct b_paragraph_format {
b_paragraph_format_flags p_flags;
unsigned int p_left_margin;
unsigned int p_right_margin;
unsigned int p_line_length;
} b_paragraph_format;
typedef enum b_print_format {
B_PRINT_NORMAL = 0,
B_PRINT_I,
B_PRINT_WARN,
B_PRINT_ERR,
} b_print_format;
BLUE_API b_status b_term_get_dimensions(FILE *fp, unsigned int *w, unsigned int *h);
BLUE_API b_status b_print(b_print_format format, const char *str, ...);
BLUE_API b_status b_print_paragraph(
const char *str, FILE *fp, b_paragraph_format *format);
BLUE_API int b_fputs(const char *str, FILE *fp);
BLUE_API int b_printf(const char *format, ...);
BLUE_API int b_fprintf(FILE *fp, const char *format, ...);
#endif #endif

View File

@@ -0,0 +1,45 @@
#ifndef BLUELIB_TERM_PRINT_H_
#define BLUELIB_TERM_PRINT_H_
#include <blue/core/misc.h>
#include <blue/core/status.h>
#include <blue/term/tty.h>
#include <stdarg.h>
#define b_i(...) b_print(B_PRINT_I, __VA_ARGS__)
#define b_warn(...) b_print(B_PRINT_WARN, __VA_ARGS__)
#define b_err(...) b_print(B_PRINT_ERR, __VA_ARGS__)
struct b_tty;
typedef enum b_paragraph_format_flags {
B_PARAGRAPH_DONT_INDENT_FIRST_LINE = 0x01u,
B_PARAGRAPH_HYPHENATE = 0x02u,
B_PARAGRAPH_DOUBLE_LINE_BREAK = 0x04u,
B_PARAGRAPH_MULTI_LINE_BREAK = 0x08u,
} b_paragraph_format_flags;
typedef struct b_paragraph_format {
b_paragraph_format_flags p_flags;
unsigned int p_left_margin;
unsigned int p_right_margin;
unsigned int p_line_length;
} b_paragraph_format;
typedef enum b_print_format {
B_PRINT_NORMAL = 0,
B_PRINT_I,
B_PRINT_WARN,
B_PRINT_ERR,
} b_print_format;
BLUE_API b_status b_print(b_print_format format, const char *str, ...);
BLUE_API b_status b_print_paragraph(
const char *str, struct b_tty *tty, b_paragraph_format *format);
BLUE_API int b_putc(char c);
BLUE_API int b_puts(const char* s);
BLUE_API int b_printf(const char* format, ...);
#endif

View File

@@ -0,0 +1,179 @@
#ifndef BLUELIB_TERM_TTY_H_
#define BLUELIB_TERM_TTY_H_
#include <blue/core/status.h>
#include <blue/core/misc.h>
#include <stdint.h>
#include <stdbool.h>
#define b_stdtty (z__b_tty_get_std())
#define b_stderr (z__b_tty_get_err())
#define B_TTY_CTRL_KEY(c) ((c) | B_MOD_CTRL)
#define B_MAKE_VMODE(fg, bg, a) \
{ \
.v_fg = fg, .v_bg = bg, .v_attrib = (a) \
}
#define B_MAKE_COLOUR_DEFAULT(v) \
{ \
.c_mode = TTY_COLOUR_NONE, \
}
#define B_MAKE_COLOUR_16(v) \
{ \
.c_mode = TTY_COLOUR_16, .c_16 = {.value = (v) } \
}
#define B_MAKE_COLOUR_256(v) \
{ \
.c_mode = TTY_COLOUR_256, .c_256 = {.value = (v) } \
}
#define B_MAKE_COLOUR_TRUE(cr, cg, cb) \
{ \
.c_mode = TTY_COLOUR_TRUE, .c_true \
= {.r = (cr), \
.g = (cg), \
.b = (cb) } \
}
typedef struct b_tty b_tty;
typedef uint32_t b_keycode;
/* codepoints U+F0000 to U+FFFFD are reserved areas in Unicode, free for
* application use. store special keycodes here */
enum {
B_KEY_ARROW_LEFT = 0xF0000,
B_KEY_ARROW_RIGHT,
B_KEY_ARROW_UP,
B_KEY_ARROW_DOWN,
B_KEY_BACKSPACE,
B_KEY_RETURN,
B_MOD_CTRL = 0x10000000,
B_KEY_EOF = 0xFFFFFFFF,
};
typedef enum b_tty_colour16 {
B_TTY_COLOUR16_BLACK = 0,
B_TTY_COLOUR16_RED,
B_TTY_COLOUR16_GREEN,
B_TTY_COLOUR16_YELLOW,
B_TTY_COLOUR16_BLUE,
B_TTY_COLOUR16_MAGENTA,
B_TTY_COLOUR16_CYAN,
B_TTY_COLOUR16_WHITE,
B_TTY_COLOUR16_BRIGHT_BLACK,
B_TTY_COLOUR16_BRIGHT_RED,
B_TTY_COLOUR16_BRIGHT_GREEN,
B_TTY_COLOUR16_BRIGHT_YELLOW,
B_TTY_COLOUR16_BRIGHT_BLUE,
B_TTY_COLOUR16_BRIGHT_MAGENTA,
B_TTY_COLOUR16_BRIGHT_CYAN,
B_TTY_COLOUR16_BRIGHT_WHITE,
} b_tty_colour16;
typedef enum b_tty_colour_mode {
B_TTY_COLOUR_NONE = 0,
B_TTY_COLOUR_16,
B_TTY_COLOUR_256,
B_TTY_COLOUR_TRUE,
} b_tty_colour_mode;
typedef enum b_tty_position_base {
B_TTY_POS_START,
B_TTY_POS_CURSOR,
} b_tty_position_base;
typedef enum b_tty_attrib {
B_TTY_ATTRIB_NORMAL = 0x00u,
B_TTY_ATTRIB_BOLD = 0x01u,
B_TTY_ATTRIB_UNDERLINE = 0x02u,
B_TTY_ATTRIB_ITALIC = 0x04u,
B_TTY_ATTRIB_INVERT = 0x08u,
} b_tty_attrib;
typedef enum b_tty_clear_mode {
B_TTY_CLEAR_LINE = 0x01u,
B_TTY_CLEAR_SCREEN = 0x02,
B_TTY_CLEAR_TO_CURSOR = 0x0100u,
B_TTY_CLEAR_FROM_CURSOR = 0x0200u,
B_TTY_CLEAR_ALL = 0x0400u,
} b_tty_clear_mode;
typedef enum b_tty_mode {
B_TTY_CANONICAL = 0x00u,
B_TTY_RAW = 0x01u,
} b_tty_mode;
typedef enum b_tty_print_flags {
B_TTY_DISABLE_FORMATTING = 0x01u,
B_TTY_DISABLE_INTERPOLATED_FORMATTING = 0x02u,
} b_tty_print_flags;
typedef struct b_tty_colour {
enum b_tty_colour_mode c_mode;
union {
struct {
uint8_t value;
} c_16;
struct {
uint8_t value;
} c_256;
struct {
uint8_t r;
uint8_t g;
uint8_t b;
} c_true;
};
} b_tty_colour;
typedef struct b_tty_vmode {
enum b_tty_attrib v_attrib;
struct b_tty_colour v_fg;
struct b_tty_colour v_bg;
} b_tty_vmode;
BLUE_API b_tty *z__b_tty_get_std(void);
BLUE_API b_tty *z__b_tty_get_err(void);
static inline unsigned int b_keycode_get_key(b_keycode v)
{
return v & 0x0FFFFFFF;
}
BLUE_API bool b_tty_is_interactive(const struct b_tty *tty);
BLUE_API void b_tty_set_mode(struct b_tty *tty, enum b_tty_mode mode);
BLUE_API void b_tty_set_vmode(struct b_tty *tty, const struct b_tty_vmode *vmode);
BLUE_API void b_tty_reset_vmode(struct b_tty *tty);
BLUE_API enum b_status b_tty_get_dimensions(
struct b_tty *tty, unsigned int *w, unsigned int *h);
BLUE_API enum b_status b_tty_get_cursor_position(
struct b_tty *tty, unsigned int *x, unsigned int *y);
BLUE_API b_keycode b_tty_read_key(struct b_tty *tty);
BLUE_API void b_tty_move_cursor_x(
struct b_tty *tty, enum b_tty_position_base base, int pos);
BLUE_API void b_tty_move_cursor_y(
struct b_tty *tty, enum b_tty_position_base base, int pos);
BLUE_API void b_tty_clear(struct b_tty *tty, enum b_tty_clear_mode mode);
BLUE_API int b_tty_putc(
struct b_tty *tty, enum b_tty_print_flags flags, char c);
BLUE_API int b_tty_puts(
struct b_tty *tty, enum b_tty_print_flags flags, const char *s);
BLUE_API int b_tty_printf(
struct b_tty *tty, enum b_tty_print_flags flags, const char *s, ...);
BLUE_API int b_tty_vprintf(
struct b_tty *tty, enum b_tty_print_flags flags, const char *s, va_list args);
#endif

View File

@@ -1,7 +1,7 @@
#include "print.h" #include "print.h"
#include <blue/object/string.h> #include <blue/object/string.h>
#include <blue/term.h> #include <blue/term/print.h>
#include <ctype.h> #include <ctype.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
@@ -10,17 +10,17 @@
#define DEFAULT_PARAGRAPH_WIDTH 160 #define DEFAULT_PARAGRAPH_WIDTH 160
static void indent( static void indent(
struct b_paragraph_format *format, FILE *fp, unsigned int margin, bool tty) struct b_paragraph_format *format, struct b_tty *tty, unsigned int margin)
{ {
unsigned int x = 0; unsigned int x = 0;
while (x < margin) { while (x < margin) {
fputs(" ", fp); b_tty_puts(tty, 0, " ");
x++; x++;
} }
} }
static unsigned int extract_line( static unsigned int extract_line(
const char **sp, unsigned int line_length, b_string *out, const char **sp, unsigned int line_length, struct b_string *out,
struct b_paragraph_format *format) struct b_paragraph_format *format)
{ {
const char *start = *sp; const char *start = *sp;
@@ -96,10 +96,10 @@ static unsigned int extract_line(
} }
static b_status print_paragraph_tty( static b_status print_paragraph_tty(
const char *str, FILE *fp, struct b_paragraph_format *format) const char *str, struct b_tty *tty, struct b_paragraph_format *format)
{ {
unsigned int w = 0, h = 0; unsigned int w = 0, h = 0;
z__b_stream_dimensions(fp, &w, &h); b_tty_get_dimensions(tty, &w, &h);
if (!w) { if (!w) {
w = DEFAULT_PARAGRAPH_WIDTH; w = DEFAULT_PARAGRAPH_WIDTH;
@@ -119,7 +119,7 @@ static b_status print_paragraph_tty(
} }
if (!(format->p_flags & B_PARAGRAPH_DONT_INDENT_FIRST_LINE)) { if (!(format->p_flags & B_PARAGRAPH_DONT_INDENT_FIRST_LINE)) {
indent(format, fp, left_margin, true); indent(format, tty, left_margin);
} }
b_string *line = b_string_create(); b_string *line = b_string_create();
@@ -128,7 +128,7 @@ static b_status print_paragraph_tty(
while (str) { while (str) {
if (*str == '\n') { if (*str == '\n') {
if (format->p_flags & B_PARAGRAPH_DOUBLE_LINE_BREAK) { if (format->p_flags & B_PARAGRAPH_DOUBLE_LINE_BREAK) {
fputc('\n', fp); b_tty_putc(tty, 0, '\n');
} }
str++; str++;
@@ -136,7 +136,7 @@ static b_status print_paragraph_tty(
while (*str == '\n') { while (*str == '\n') {
if (format->p_flags & B_PARAGRAPH_MULTI_LINE_BREAK) { if (format->p_flags & B_PARAGRAPH_MULTI_LINE_BREAK) {
fputc('\n', fp); b_tty_putc(tty, 0, '\n');
} }
str++; str++;
@@ -151,11 +151,11 @@ static b_status print_paragraph_tty(
} }
if (need_indent) { if (need_indent) {
indent(format, fp, left_margin, false); indent(format, tty, left_margin);
} }
b_fputs(b_string_ptr(line), fp); b_tty_puts(tty, 0, b_string_ptr(line));
fputc('\n', fp); b_tty_putc(tty, 0, '\n');
need_indent = true; need_indent = true;
} }
@@ -171,11 +171,7 @@ static b_status print_paragraph_file(
} }
b_status b_print_paragraph( b_status b_print_paragraph(
const char *str, FILE *fp, struct b_paragraph_format *format) const char *str, struct b_tty *fp, struct b_paragraph_format *format)
{ {
if (z__b_stream_is_tty(fp)) {
return print_paragraph_tty(str, fp, format);
}
return print_paragraph_tty(str, fp, format); return print_paragraph_tty(str, fp, format);
} }

View File

@@ -1,7 +1,7 @@
#include "print.h" #include "print.h"
#include <blue/core/hash.h> #include <blue/core/hash.h>
#include <blue/term.h> #include <blue/term/print.h>
#include <ctype.h> #include <ctype.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdbool.h> #include <stdbool.h>
@@ -25,218 +25,7 @@
#define F_RESET "[reset]" #define F_RESET "[reset]"
typedef b_status (*print_function)(FILE *fp, const char *s); typedef b_status (*print_function)(struct b_tty *tty, const char *s);
#define MOD_HASH_BLACK 0x4b5dd0abbc6fc1e4
#define MOD_HASH_RED 0x89e9be1960f4c21c
#define MOD_HASH_GREEN 0x0f40f029637fecbc
#define MOD_HASH_YELLOW 0x8346a574925e75a9
#define MOD_HASH_BLUE 0xc5ccd29bc2dda64d
#define MOD_HASH_MAGENTA 0x6c90e772edbc8708
#define MOD_HASH_CYAN 0x70ae2e90c1bce27a
#define MOD_HASH_WHITE 0xced973885856e206
#define MOD_HASH_BG_BLACK 0xd87a8f2d9d394432
#define MOD_HASH_BG_RED 0x145b1e4366c7d7aa
#define MOD_HASH_BG_GREEN 0xa00b8541d3b1e55a
#define MOD_HASH_BG_YELLOW 0x98b030fd86e3b3cf
#define MOD_HASH_BG_BLUE 0xa15529109506b5df
#define MOD_HASH_BG_MAGENTA 0x86dbda99bcc86222
#define MOD_HASH_BG_CYAN 0xf16a3104cf61a098
#define MOD_HASH_BG_WHITE 0x3408c46ab5836674
#define MOD_HASH_BOLD 0xcd32ea9bc6b26ff6
#define MOD_HASH_BRIGHT 0xb5cca637f5a2b385
#define MOD_HASH_ULINE 0x141fe741e9f8c22a
#define MOD_HASH_ITALIC 0x69d5e5f057d8992d
#define MOD_HASH_INVERT 0xab4ab85ddd6232e1
#define MOD_HASH_RESET 0x0f136ff2c086b760
#define COMPARE_MOD_NAME(ss, sd, hs, hd) ((hs) == (hd) && !strcmp(ss, sd))
b_status b_term_get_dimensions(FILE *fp, unsigned int *w, unsigned int *h)
{
return z__b_stream_dimensions(fp, w, h) == 0 ? B_SUCCESS
: B_ERR_NOT_SUPPORTED;
}
static void apply_modifier(FILE *fp, const char *modifier, unsigned int *mod_flagp)
{
uint64_t mod_hash = b_hash_string(modifier);
unsigned int mod_flags = *mod_flagp;
if (COMPARE_MOD_NAME(modifier, "black", mod_hash, MOD_HASH_BLACK)) {
mod_flags = Z__B_STREAM_MOD_CLEAR_FG_COLOUR(mod_flags);
mod_flags |= Z__B_STREAM_MOD_BLACK;
}
if (COMPARE_MOD_NAME(modifier, "red", mod_hash, MOD_HASH_RED)) {
mod_flags = Z__B_STREAM_MOD_CLEAR_FG_COLOUR(mod_flags);
mod_flags |= Z__B_STREAM_MOD_RED;
}
if (COMPARE_MOD_NAME(modifier, "green", mod_hash, MOD_HASH_GREEN)) {
mod_flags = Z__B_STREAM_MOD_CLEAR_FG_COLOUR(mod_flags);
mod_flags |= Z__B_STREAM_MOD_GREEN;
}
if (COMPARE_MOD_NAME(modifier, "yellow", mod_hash, MOD_HASH_YELLOW)) {
mod_flags = Z__B_STREAM_MOD_CLEAR_FG_COLOUR(mod_flags);
mod_flags |= Z__B_STREAM_MOD_RED | Z__B_STREAM_MOD_GREEN;
}
if (COMPARE_MOD_NAME(modifier, "blue", mod_hash, MOD_HASH_BLUE)) {
mod_flags = Z__B_STREAM_MOD_CLEAR_FG_COLOUR(mod_flags);
mod_flags |= Z__B_STREAM_MOD_BLUE;
}
if (COMPARE_MOD_NAME(modifier, "magenta", mod_hash, MOD_HASH_MAGENTA)) {
mod_flags = Z__B_STREAM_MOD_CLEAR_FG_COLOUR(mod_flags);
mod_flags |= Z__B_STREAM_MOD_RED | Z__B_STREAM_MOD_BLUE;
}
if (COMPARE_MOD_NAME(modifier, "cyan", mod_hash, MOD_HASH_CYAN)) {
mod_flags = Z__B_STREAM_MOD_CLEAR_FG_COLOUR(mod_flags);
mod_flags |= Z__B_STREAM_MOD_GREEN | Z__B_STREAM_MOD_BLUE;
}
if (COMPARE_MOD_NAME(modifier, "white", mod_hash, MOD_HASH_WHITE)) {
mod_flags = Z__B_STREAM_MOD_CLEAR_FG_COLOUR(mod_flags);
mod_flags |= Z__B_STREAM_MOD_RED | Z__B_STREAM_MOD_GREEN
| Z__B_STREAM_MOD_BLUE;
}
if (COMPARE_MOD_NAME(modifier, "bg_black", mod_hash, MOD_HASH_BG_BLACK)) {
mod_flags = Z__B_STREAM_MOD_CLEAR_BG_COLOUR(mod_flags);
mod_flags |= Z__B_STREAM_MOD_BG_BLACK;
}
if (COMPARE_MOD_NAME(modifier, "bg_red", mod_hash, MOD_HASH_BG_RED)) {
mod_flags = Z__B_STREAM_MOD_CLEAR_BG_COLOUR(mod_flags);
mod_flags |= Z__B_STREAM_MOD_BG_RED;
}
if (COMPARE_MOD_NAME(modifier, "bg_green", mod_hash, MOD_HASH_BG_GREEN)) {
mod_flags = Z__B_STREAM_MOD_CLEAR_BG_COLOUR(mod_flags);
mod_flags |= Z__B_STREAM_MOD_BG_GREEN;
}
if (COMPARE_MOD_NAME(modifier, "bg_yellow", mod_hash, MOD_HASH_BG_YELLOW)) {
mod_flags = Z__B_STREAM_MOD_CLEAR_BG_COLOUR(mod_flags);
mod_flags |= Z__B_STREAM_MOD_BG_RED | Z__B_STREAM_MOD_BG_GREEN;
}
if (COMPARE_MOD_NAME(modifier, "bg_blue", mod_hash, MOD_HASH_BG_BLUE)) {
mod_flags = Z__B_STREAM_MOD_CLEAR_BG_COLOUR(mod_flags);
mod_flags |= Z__B_STREAM_MOD_BG_BLUE;
}
if (COMPARE_MOD_NAME(modifier, "bg_magenta", mod_hash, MOD_HASH_BG_MAGENTA)) {
mod_flags = Z__B_STREAM_MOD_CLEAR_BG_COLOUR(mod_flags);
mod_flags |= Z__B_STREAM_MOD_BG_RED | Z__B_STREAM_MOD_BG_BLUE;
}
if (COMPARE_MOD_NAME(modifier, "bg_cyan", mod_hash, MOD_HASH_BG_CYAN)) {
mod_flags = Z__B_STREAM_MOD_CLEAR_BG_COLOUR(mod_flags);
mod_flags |= Z__B_STREAM_MOD_BG_GREEN | Z__B_STREAM_MOD_BG_BLUE;
}
if (COMPARE_MOD_NAME(modifier, "bg_white", mod_hash, MOD_HASH_BG_WHITE)) {
mod_flags = Z__B_STREAM_MOD_CLEAR_BG_COLOUR(mod_flags);
mod_flags |= Z__B_STREAM_MOD_BG_RED | Z__B_STREAM_MOD_BG_GREEN
| Z__B_STREAM_MOD_BG_BLUE;
}
if (COMPARE_MOD_NAME(modifier, "bold", mod_hash, MOD_HASH_BOLD)) {
mod_flags |= Z__B_STREAM_MOD_BOLD;
}
if (COMPARE_MOD_NAME(modifier, "bright", mod_hash, MOD_HASH_BRIGHT)) {
mod_flags |= Z__B_STREAM_MOD_BRIGHT;
}
if (COMPARE_MOD_NAME(modifier, "uline", mod_hash, MOD_HASH_ULINE)) {
mod_flags |= Z__B_STREAM_MOD_ULINE;
}
if (COMPARE_MOD_NAME(modifier, "italic", mod_hash, MOD_HASH_ITALIC)) {
mod_flags |= Z__B_STREAM_MOD_ITALIC;
}
if (COMPARE_MOD_NAME(modifier, "invert", mod_hash, MOD_HASH_INVERT)) {
mod_flags |= Z__B_STREAM_MOD_INVERT;
}
if (COMPARE_MOD_NAME(modifier, "reset", mod_hash, MOD_HASH_RESET)) {
mod_flags = Z__B_STREAM_MOD_RESET;
}
*mod_flagp = mod_flags;
}
static int print(FILE *fp, const char *str)
{
int len = 0;
char ctrl_buf[128];
unsigned int ctrl_len = 0;
for (size_t i = 0; str[i];) {
char c = str[i++];
if (c != '[') {
fputc(c, fp);
len++;
continue;
}
c = str[i++];
if (c == '[') {
fputc(c, fp);
len++;
continue;
}
ctrl_buf[ctrl_len++] = c;
unsigned int mod_flags = 0;
while (true) {
c = str[i++];
if (isspace(c)) {
continue;
}
if (c == '\0') {
break;
}
if (c == ']') {
ctrl_buf[ctrl_len] = '\0';
apply_modifier(fp, ctrl_buf, &mod_flags);
ctrl_len = 0;
ctrl_buf[0] = '\0';
break;
}
if (c == ',') {
ctrl_buf[ctrl_len] = '\0';
apply_modifier(fp, ctrl_buf, &mod_flags);
ctrl_len = 0;
ctrl_buf[0] = '\0';
continue;
}
if (ctrl_len < sizeof ctrl_buf - 1) {
ctrl_buf[ctrl_len++] = c;
}
}
z__b_stream_set_modifier(fp, mod_flags);
}
return len;
}
static int print_no_ansi(FILE *fp, const char *str) static int print_no_ansi(FILE *fp, const char *str)
{ {
@@ -257,52 +46,52 @@ static int print_no_ansi(FILE *fp, const char *str)
return out; return out;
} }
static b_status print_normal(FILE *fp, const char *s) static b_status print_normal(struct b_tty *tty, const char *s)
{ {
return B_SUCCESS; return B_SUCCESS;
} }
static b_status print_i(FILE *fp, const char *s) static b_status print_i(struct b_tty *tty, const char *s)
{ {
b_paragraph_format format = {0}; struct b_paragraph_format format = {0};
format.p_left_margin = 5; format.p_left_margin = 5;
format.p_right_margin = 5; format.p_right_margin = 5;
format.p_flags = B_PARAGRAPH_DONT_INDENT_FIRST_LINE format.p_flags = B_PARAGRAPH_DONT_INDENT_FIRST_LINE
| B_PARAGRAPH_MULTI_LINE_BREAK; | B_PARAGRAPH_MULTI_LINE_BREAK;
// printf(F_CYN_BG F_BLACK " i " F_RESET " "); // printf(F_CYN_BG F_BLACK " i " F_RESET " ");
b_fprintf(fp, F_CYN_BOLD " i:" F_RESET " "); b_tty_puts(tty, 0, F_CYN_BOLD " i:" F_RESET " ");
b_print_paragraph(s, fp, &format); b_print_paragraph(s, tty, &format);
return B_SUCCESS; return B_SUCCESS;
} }
static b_status print_warn(FILE *fp, const char *s) static b_status print_warn(struct b_tty *tty, const char *s)
{ {
b_paragraph_format format = {0}; struct b_paragraph_format format = {0};
format.p_left_margin = 5; format.p_left_margin = 5;
format.p_right_margin = 5; format.p_right_margin = 5;
format.p_flags = B_PARAGRAPH_DONT_INDENT_FIRST_LINE format.p_flags = B_PARAGRAPH_DONT_INDENT_FIRST_LINE
| B_PARAGRAPH_MULTI_LINE_BREAK; | B_PARAGRAPH_MULTI_LINE_BREAK;
// printf(F_YEL_BG F_BLACK " Warn " F_RESET " "); // printf(F_YEL_BG F_BLACK " Warn " F_RESET " ");
b_fprintf(fp, F_YEL_BOLD " !!" F_RESET " "); b_tty_puts(tty, 0, F_YEL_BOLD " !!" F_RESET " ");
b_print_paragraph(s, fp, &format); b_print_paragraph(s, tty, &format);
return B_SUCCESS; return B_SUCCESS;
} }
static b_status print_err(FILE *fp, const char *s) static b_status print_err(struct b_tty *tty, const char *s)
{ {
b_paragraph_format format = {0}; struct b_paragraph_format format = {0};
format.p_left_margin = 5; format.p_left_margin = 5;
format.p_right_margin = 5; format.p_right_margin = 5;
format.p_flags = B_PARAGRAPH_DONT_INDENT_FIRST_LINE format.p_flags = B_PARAGRAPH_DONT_INDENT_FIRST_LINE
| B_PARAGRAPH_MULTI_LINE_BREAK; | B_PARAGRAPH_MULTI_LINE_BREAK;
// printf(F_RED_BG F_BLACK " Err " F_RESET " "); // printf(F_RED_BG F_BLACK " Err " F_RESET " ");
b_fprintf(fp, F_RED_BOLD "Err:" F_RESET " "); b_tty_puts(tty, 0, F_RED_BOLD "Err:" F_RESET " ");
b_print_paragraph(s, fp, &format); b_print_paragraph(s, tty, &format);
return B_SUCCESS; return B_SUCCESS;
} }
@@ -334,47 +123,25 @@ b_status b_print(b_print_format format, const char *str, ...)
return B_ERR_INVALID_ARGUMENT; return B_ERR_INVALID_ARGUMENT;
} }
return printer(stdout, buf); return printer(b_stdtty, buf);
} }
static int b_vfprintf(FILE *fp, const char *format, va_list arg) int b_putc(char c)
{ {
char str[1024]; return b_tty_putc(b_stdtty, 0, c);
vsnprintf(str, sizeof str, format, arg);
if (z__b_stream_is_tty(fp)) {
return print(fp, str);
}
return print_no_ansi(fp, str);
} }
int b_fputs(const char *str, FILE *fp) int b_puts(const char* s)
{ {
if (z__b_stream_is_tty(fp)) { return b_tty_puts(b_stdtty, 0, s);
return print(fp, str);
}
return print_no_ansi(fp, str);
} }
int b_printf(const char *format, ...) int b_printf(const char* format, ...)
{ {
va_list arg; va_list arg;
va_start(arg, format); va_start(arg, format);
int len = b_vfprintf(stdout, format, arg); int x = b_tty_vprintf(
b_stdtty, B_TTY_DISABLE_INTERPOLATED_FORMATTING, format, arg);
va_end(arg); va_end(arg);
return x;
return len;
}
int b_fprintf(FILE *fp, const char *format, ...)
{
va_list arg;
va_start(arg, format);
int len = b_vfprintf(fp, format, arg);
va_end(arg);
return len;
} }

996
term/printf.c Normal file
View File

@@ -0,0 +1,996 @@
///////////////////////////////////////////////////////////////////////////////
// \author (c) Marco Paland (info@paland.com)
// 2014-2019, PALANDesign Hannover, Germany
//
// \license The MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on
// embedded systems with a very limited resources. These routines are thread
// safe and reentrant!
// Use this instead of the bloated standard/newlib printf cause these use
// malloc for printf (and may not be thread safe).
//
///////////////////////////////////////////////////////////////////////////////
#include "printf.h"
#include <blue/term/tty.h>
#include <stdbool.h>
#include <stdint.h>
// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the
// printf_config.h header file
// default: undefined
#ifdef PRINTF_INCLUDE_CONFIG_H
#include "printf_config.h"
#endif
// 'ntoa' conversion buffer size, this must be big enough to hold one converted
// numeric number including padded zeros (dynamically created on stack)
// default: 32 byte
#ifndef PRINTF_NTOA_BUFFER_SIZE
#define PRINTF_NTOA_BUFFER_SIZE 32U
#endif
// 'ftoa' conversion buffer size, this must be big enough to hold one converted
// float number including padded zeros (dynamically created on stack)
// default: 32 byte
#ifndef PRINTF_FTOA_BUFFER_SIZE
#define PRINTF_FTOA_BUFFER_SIZE 32U
#endif
// support for the floating point type (%f)
// default: activated
#ifndef PRINTF_DISABLE_SUPPORT_FLOAT
#define PRINTF_SUPPORT_FLOAT
#endif
// support for exponential floating point notation (%e/%g)
// default: activated
#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL
#define PRINTF_SUPPORT_EXPONENTIAL
#endif
// define the default floating point precision
// default: 6 digits
#ifndef PRINTF_DEFAULT_FLOAT_PRECISION
#define PRINTF_DEFAULT_FLOAT_PRECISION 6U
#endif
// define the largest float suitable to print with %f
// default: 1e9
#ifndef PRINTF_MAX_FLOAT
#define PRINTF_MAX_FLOAT 1e9
#endif
// support for the long long types (%llu or %p)
// default: activated
#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG
#define PRINTF_SUPPORT_LONG_LONG
#endif
// support for the ptrdiff_t type (%t)
// ptrdiff_t is normally defined in <stddef.h> as long or long long type
// default: activated
#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T
#define PRINTF_SUPPORT_PTRDIFF_T
#endif
///////////////////////////////////////////////////////////////////////////////
// internal flag definitions
#define FLAGS_ZEROPAD (1U << 0U)
#define FLAGS_LEFT (1U << 1U)
#define FLAGS_PLUS (1U << 2U)
#define FLAGS_SPACE (1U << 3U)
#define FLAGS_HASH (1U << 4U)
#define FLAGS_UPPERCASE (1U << 5U)
#define FLAGS_CHAR (1U << 6U)
#define FLAGS_SHORT (1U << 7U)
#define FLAGS_LONG (1U << 8U)
#define FLAGS_LONG_LONG (1U << 9U)
#define FLAGS_PRECISION (1U << 10U)
#define FLAGS_ADAPT_EXP (1U << 11U)
// import float.h for DBL_MAX
#if defined(PRINTF_SUPPORT_FLOAT)
#include <float.h>
#endif
struct out_tty_args {
struct b_tty *tty;
enum b_tty_print_flags flags;
bool format_ch;
};
// output function type
typedef void (*out_fct_type)(
char character, struct out_tty_args *args);
// wrapper (used as buffer) for output function type
typedef struct {
void (*fct)(char character, void *arg);
void *arg;
} out_fct_wrap_type;
// internal buffer output
static inline void _out_buffer(
char character, void *buffer, size_t idx, size_t maxlen)
{
if (idx < maxlen) {
((char *)buffer)[idx] = character;
}
}
// internal null output
static inline void _out_null(char character, void *buffer, size_t idx, size_t maxlen)
{
(void)character;
(void)buffer;
(void)idx;
(void)maxlen;
}
// internal _putchar wrapper
static inline void _out_char(char character, void *buffer, size_t idx, size_t maxlen)
{
(void)buffer;
(void)idx;
(void)maxlen;
if (character) {
_putchar(character);
}
}
// internal output function wrapper
static inline void _out_fct(char character, void *buffer, size_t idx, size_t maxlen)
{
(void)idx;
(void)maxlen;
if (character) {
// buffer is the output fct pointer
((out_fct_wrap_type *)buffer)
->fct(character, ((out_fct_wrap_type *)buffer)->arg);
}
}
// internal secure strlen
// \return The length of the string (excluding the terminating 0) limited by 'maxsize'
static inline unsigned int _strnlen_s(const char *str, size_t maxsize)
{
const char *s;
for (s = str; *s && maxsize--; ++s)
;
return (unsigned int)(s - str);
}
// internal test if char is a digit (0-9)
// \return true if char is a digit
static inline bool _is_digit(char ch)
{
return (ch >= '0') && (ch <= '9');
}
// internal ASCII string to unsigned int conversion
static unsigned int _atoi(const char **str)
{
unsigned int i = 0U;
while (_is_digit(**str)) {
i = i * 10U + (unsigned int)(*((*str)++) - '0');
}
return i;
}
// output the specified string in reverse, taking care of any zero-padding
static size_t _out_rev(
out_fct_type out, struct out_tty_args *args,
const char *buf, size_t len, unsigned int width, unsigned int flags)
{
size_t idx = 0;
// pad spaces up to given width
if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
for (size_t i = len; i < width; i++) {
out(' ', args);
}
}
// reverse string
while (len) {
out(buf[--len], args);
}
// append pad spaces up to given width
if (flags & FLAGS_LEFT) {
while (idx < width) {
out(' ', args);
idx++;
}
}
return idx;
}
// internal itoa format
static size_t _ntoa_format(
out_fct_type out, struct out_tty_args *args, char *buf,
size_t len, bool negative, unsigned int base, unsigned int prec,
unsigned int width, unsigned int flags)
{
// pad leading zeros
if (!(flags & FLAGS_LEFT)) {
if (width && (flags & FLAGS_ZEROPAD)
&& (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
width--;
}
while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
while ((flags & FLAGS_ZEROPAD) && (len < width)
&& (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
}
// handle hash
if (flags & FLAGS_HASH) {
if (!(flags & FLAGS_PRECISION) && len
&& ((len == prec) || (len == width))) {
len--;
if (len && (base == 16U)) {
len--;
}
}
if ((base == 16U) && !(flags & FLAGS_UPPERCASE)
&& (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = 'x';
} else if (
(base == 16U) && (flags & FLAGS_UPPERCASE)
&& (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = 'X';
} else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = 'b';
}
if (len < PRINTF_NTOA_BUFFER_SIZE) {
buf[len++] = '0';
}
}
if (len < PRINTF_NTOA_BUFFER_SIZE) {
if (negative) {
buf[len++] = '-';
} else if (flags & FLAGS_PLUS) {
buf[len++] = '+'; // ignore the space if the '+' exists
} else if (flags & FLAGS_SPACE) {
buf[len++] = ' ';
}
}
return _out_rev(out, args, buf, len, width, flags);
}
// internal itoa for 'long' type
static size_t _ntoa_long(
out_fct_type out, struct out_tty_args *args,
unsigned long value, bool negative, unsigned long base,
unsigned int prec, unsigned int width, unsigned int flags)
{
char buf[PRINTF_NTOA_BUFFER_SIZE];
size_t len = 0U;
// no hash for 0 values
if (!value) {
flags &= ~FLAGS_HASH;
}
// write if precision != 0 and value is != 0
if (!(flags & FLAGS_PRECISION) || value) {
do {
const char digit = (char)(value % base);
buf[len++] = digit < 10
? '0' + digit
: (flags & FLAGS_UPPERCASE ? 'A' : 'a')
+ digit - 10;
value /= base;
} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
}
return _ntoa_format(
out, args, buf, len, negative,
(unsigned int)base, prec, width, flags);
}
// internal itoa for 'long long' type
#if defined(PRINTF_SUPPORT_LONG_LONG)
static size_t _ntoa_long_long(
out_fct_type out, struct out_tty_args *args,
unsigned long long value, bool negative, unsigned long long base,
unsigned int prec, unsigned int width, unsigned int flags)
{
char buf[PRINTF_NTOA_BUFFER_SIZE];
size_t len = 0U;
// no hash for 0 values
if (!value) {
flags &= ~FLAGS_HASH;
}
// write if precision != 0 and value is != 0
if (!(flags & FLAGS_PRECISION) || value) {
do {
const char digit = (char)(value % base);
buf[len++] = digit < 10
? '0' + digit
: (flags & FLAGS_UPPERCASE ? 'A' : 'a')
+ digit - 10;
value /= base;
} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
}
return _ntoa_format(
out, args, buf, len, negative,
(unsigned int)base, prec, width, flags);
}
#endif // PRINTF_SUPPORT_LONG_LONG
#if defined(PRINTF_SUPPORT_FLOAT)
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT
static size_t _etoa(
out_fct_type out, struct out_tty_args *args, double value,
unsigned int prec, unsigned int width, unsigned int flags);
#endif
// internal ftoa for fixed decimal floating point
static size_t _ftoa(
out_fct_type out, struct out_tty_args *args, double value,
unsigned int prec, unsigned int width, unsigned int flags)
{
char buf[PRINTF_FTOA_BUFFER_SIZE];
size_t len = 0U;
double diff = 0.0;
// powers of 10
static const double pow10[]
= {1, 10, 100, 1000, 10000,
100000, 1000000, 10000000, 100000000, 1000000000};
// test for special values
if (value != value)
return _out_rev(out, args, "nan", 3, width, flags);
if (value < -DBL_MAX)
return _out_rev(out, args, "fni-", 4, width, flags);
if (value > DBL_MAX)
return _out_rev(
out, args,
(flags & FLAGS_PLUS) ? "fni+" : "fni",
(flags & FLAGS_PLUS) ? 4U : 3U, width, flags);
// test for very large values
// standard printf behavior is to print EVERY whole number digit --
// which could be 100s of characters overflowing your buffers == bad
if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) {
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
return _etoa(out, args, value, prec, width, flags);
#else
return 0U;
#endif
}
// test for negative
bool negative = false;
if (value < 0) {
negative = true;
value = 0 - value;
}
// set default precision, if not set explicitly
if (!(flags & FLAGS_PRECISION)) {
prec = PRINTF_DEFAULT_FLOAT_PRECISION;
}
// limit precision to 9, cause a prec >= 10 can lead to overflow errors
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
buf[len++] = '0';
prec--;
}
int whole = (int)value;
double tmp = (value - whole) * pow10[prec];
unsigned long frac = (unsigned long)tmp;
diff = tmp - frac;
if (diff > 0.5) {
++frac;
// handle rollover, e.g. case 0.99 with prec 1 is 1.0
if (frac >= pow10[prec]) {
frac = 0;
++whole;
}
} else if (diff < 0.5) {
} else if ((frac == 0U) || (frac & 1U)) {
// if halfway, round up if odd OR if last digit is 0
++frac;
}
if (prec == 0U) {
diff = value - (double)whole;
if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) {
// exactly 0.5 and ODD, then round up
// 1.5 -> 2, but 2.5 -> 2
++whole;
}
} else {
unsigned int count = prec;
// now do fractional part, as an unsigned number
while (len < PRINTF_FTOA_BUFFER_SIZE) {
--count;
buf[len++] = (char)(48U + (frac % 10U));
if (!(frac /= 10U)) {
break;
}
}
// add extra 0s
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
buf[len++] = '0';
}
if (len < PRINTF_FTOA_BUFFER_SIZE) {
// add decimal
buf[len++] = '.';
}
}
// do whole part, number is reversed
while (len < PRINTF_FTOA_BUFFER_SIZE) {
buf[len++] = (char)(48 + (whole % 10));
if (!(whole /= 10)) {
break;
}
}
// pad leading zeros
if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
width--;
}
while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
}
if (len < PRINTF_FTOA_BUFFER_SIZE) {
if (negative) {
buf[len++] = '-';
} else if (flags & FLAGS_PLUS) {
buf[len++] = '+'; // ignore the space if the '+' exists
} else if (flags & FLAGS_SPACE) {
buf[len++] = ' ';
}
}
return _out_rev(out, args, buf, len, width, flags);
}
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <m.jasperse@gmail.com>
static size_t _etoa(
out_fct_type out, struct out_tty_args *args, double value,
unsigned int prec, unsigned int width, unsigned int flags)
{
// check for NaN and special values
if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) {
return _ftoa(out, args, value, prec, width, flags);
}
// determine the sign
const bool negative = value < 0;
if (negative) {
value = -value;
}
// default precision
if (!(flags & FLAGS_PRECISION)) {
prec = PRINTF_DEFAULT_FLOAT_PRECISION;
}
// determine the decimal exponent
// based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
union {
uint64_t U;
double F;
} conv;
conv.F = value;
int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2
conv.U = (conv.U & ((1ULL << 52U) - 1U))
| (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2)
// now approximate log10 from the log2 integer part and an expansion of ln around 1.5
int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981
+ (conv.F - 1.5) * 0.289529654602168);
// now we want to compute 10^expval but we want to be sure it won't overflow
exp2 = (int)(expval * 3.321928094887362 + 0.5);
const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
const double z2 = z * z;
conv.U = (uint64_t)(exp2 + 1023) << 52U;
// compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
// correct for rounding errors
if (value < conv.F) {
expval--;
conv.F /= 10;
}
// the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;
// in "%g" mode, "prec" is the number of *significant figures* not decimals
if (flags & FLAGS_ADAPT_EXP) {
// do we want to fall-back to "%f" mode?
if ((value >= 1e-4) && (value < 1e6)) {
if ((int)prec > expval) {
prec = (unsigned)((int)prec - expval - 1);
} else {
prec = 0;
}
flags |= FLAGS_PRECISION; // make sure _ftoa respects precision
// no characters in exponent
minwidth = 0U;
expval = 0;
} else {
// we use one sigfig for the whole part
if ((prec > 0) && (flags & FLAGS_PRECISION)) {
--prec;
}
}
}
// will everything fit?
unsigned int fwidth = width;
if (width > minwidth) {
// we didn't fall-back so subtract the characters required for the exponent
fwidth -= minwidth;
} else {
// not enough characters, so go back to default sizing
fwidth = 0U;
}
if ((flags & FLAGS_LEFT) && minwidth) {
// if we're padding on the right, DON'T pad the floating part
fwidth = 0U;
}
// rescale the float value
if (expval) {
value /= conv.F;
}
// output the floating part
size_t idx = _ftoa(out, args, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);
// output the exponent part
if (minwidth) {
// output the exponential symbol
out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', args);
// output the exponent value
idx = _ntoa_long(
out, args,
(expval < 0) ? -expval : expval, expval < 0, 10, 0,
minwidth - 1, FLAGS_ZEROPAD | FLAGS_PLUS);
// might need to right-pad spaces
if (flags & FLAGS_LEFT) {
while (idx < width) {
out(' ', args);
idx++;
}
}
}
return idx;
}
#endif // PRINTF_SUPPORT_EXPONENTIAL
#endif // PRINTF_SUPPORT_FLOAT
#define set_format_ch(args) ((args)->format_ch = true)
#define unset_format_ch(args) ((args)->format_ch = false)
// internal vsnprintf
static int _vsnprintf(
out_fct_type out, struct out_tty_args *args, const char *format,
va_list va)
{
unsigned int flags, width, precision, n;
size_t idx = 0U;
while (*format) {
// format specifier? %[flags][width][.precision][length]
if (*format != '%') {
// no
set_format_ch(args);
out(*format, args);
unset_format_ch(args);
format++;
continue;
} else {
// yes, evaluate it
format++;
}
// evaluate flags
flags = 0U;
do {
switch (*format) {
case '0':
flags |= FLAGS_ZEROPAD;
format++;
n = 1U;
break;
case '-':
flags |= FLAGS_LEFT;
format++;
n = 1U;
break;
case '+':
flags |= FLAGS_PLUS;
format++;
n = 1U;
break;
case ' ':
flags |= FLAGS_SPACE;
format++;
n = 1U;
break;
case '#':
flags |= FLAGS_HASH;
format++;
n = 1U;
break;
default:
n = 0U;
break;
}
} while (n);
// evaluate width field
width = 0U;
if (_is_digit(*format)) {
width = _atoi(&format);
} else if (*format == '*') {
const int w = va_arg(va, int);
if (w < 0) {
flags |= FLAGS_LEFT; // reverse padding
width = (unsigned int)-w;
} else {
width = (unsigned int)w;
}
format++;
}
// evaluate precision field
precision = 0U;
if (*format == '.') {
flags |= FLAGS_PRECISION;
format++;
if (_is_digit(*format)) {
precision = _atoi(&format);
} else if (*format == '*') {
const int prec = (int)va_arg(va, int);
precision = prec > 0 ? (unsigned int)prec : 0U;
format++;
}
}
// evaluate length field
switch (*format) {
case 'l':
flags |= FLAGS_LONG;
format++;
if (*format == 'l') {
flags |= FLAGS_LONG_LONG;
format++;
}
break;
case 'h':
flags |= FLAGS_SHORT;
format++;
if (*format == 'h') {
flags |= FLAGS_CHAR;
format++;
}
break;
#if defined(PRINTF_SUPPORT_PTRDIFF_T)
case 't':
flags
|= (sizeof(ptrdiff_t) == sizeof(long)
? FLAGS_LONG
: FLAGS_LONG_LONG);
format++;
break;
#endif
case 'j':
flags
|= (sizeof(intmax_t) == sizeof(long)
? FLAGS_LONG
: FLAGS_LONG_LONG);
format++;
break;
case 'z':
flags
|= (sizeof(size_t) == sizeof(long)
? FLAGS_LONG
: FLAGS_LONG_LONG);
format++;
break;
default:
break;
}
// evaluate specifier
switch (*format) {
case 'd':
case 'i':
case 'u':
case 'x':
case 'X':
case 'o':
case 'b': {
// set the base
unsigned int base;
if (*format == 'x' || *format == 'X') {
base = 16U;
} else if (*format == 'o') {
base = 8U;
} else if (*format == 'b') {
base = 2U;
} else {
base = 10U;
flags &= ~FLAGS_HASH; // no hash for dec format
}
// uppercase
if (*format == 'X') {
flags |= FLAGS_UPPERCASE;
}
// no plus or space flag for u, x, X, o, b
if ((*format != 'i') && (*format != 'd')) {
flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
}
// ignore '0' flag when precision is given
if (flags & FLAGS_PRECISION) {
flags &= ~FLAGS_ZEROPAD;
}
// convert the integer
if ((*format == 'i') || (*format == 'd')) {
// signed
if (flags & FLAGS_LONG_LONG) {
#if defined(PRINTF_SUPPORT_LONG_LONG)
const long long value
= va_arg(va, long long);
idx = _ntoa_long_long(
out, args,
(unsigned long long)(value > 0 ? value
: 0 - value),
value < 0, base, precision,
width, flags);
#endif
} else if (flags & FLAGS_LONG) {
const long value = va_arg(va, long);
idx = _ntoa_long(
out, args,
(unsigned long)(value > 0 ? value
: 0 - value),
value < 0, base, precision,
width, flags);
} else {
const int value
= (flags & FLAGS_CHAR)
? (char)va_arg(va, int)
: (flags & FLAGS_SHORT)
? (short int)va_arg(va, int)
: va_arg(va, int);
idx = _ntoa_long(
out, args,
(unsigned int)(value > 0 ? value
: 0 - value),
value < 0, base, precision,
width, flags);
}
} else {
// unsigned
if (flags & FLAGS_LONG_LONG) {
#if defined(PRINTF_SUPPORT_LONG_LONG)
idx = _ntoa_long_long(
out, args,
va_arg(va, unsigned long long),
false, base, precision, width,
flags);
#endif
} else if (flags & FLAGS_LONG) {
idx = _ntoa_long(
out, args,
va_arg(va, unsigned long), false,
base, precision, width, flags);
} else {
const unsigned int value
= (flags & FLAGS_CHAR)
? (unsigned char)va_arg(
va, unsigned int)
: (flags & FLAGS_SHORT)
? (unsigned short int)va_arg(
va, unsigned int)
: va_arg(va, unsigned int);
idx = _ntoa_long(
out, args, value,
false, base, precision, width,
flags);
}
}
format++;
break;
}
#if defined(PRINTF_SUPPORT_FLOAT)
case 'f':
case 'F':
if (*format == 'F')
flags |= FLAGS_UPPERCASE;
idx = _ftoa(
out, args, va_arg(va, double),
precision, width, flags);
format++;
break;
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
case 'e':
case 'E':
case 'g':
case 'G':
if ((*format == 'g') || (*format == 'G'))
flags |= FLAGS_ADAPT_EXP;
if ((*format == 'E') || (*format == 'G'))
flags |= FLAGS_UPPERCASE;
idx = _etoa(
out, args, va_arg(va, double),
precision, width, flags);
format++;
break;
#endif // PRINTF_SUPPORT_EXPONENTIAL
#endif // PRINTF_SUPPORT_FLOAT
case 'c': {
unsigned int l = 1U;
// pre padding
if (!(flags & FLAGS_LEFT)) {
while (l++ < width) {
out(' ', args);
}
}
// char output
out((char)va_arg(va, int), args);
// post padding
if (flags & FLAGS_LEFT) {
while (l++ < width) {
out(' ', args);
}
}
format++;
break;
}
case 's': {
const char *p = va_arg(va, char *);
unsigned int l = _strnlen_s(
p, precision ? precision : (size_t)-1);
// pre padding
if (flags & FLAGS_PRECISION) {
l = (l < precision ? l : precision);
}
if (!(flags & FLAGS_LEFT)) {
while (l++ < width) {
out(' ', args);
}
}
// string output
while ((*p != 0)
&& (!(flags & FLAGS_PRECISION) || precision--)) {
out(*(p++), args);
}
// post padding
if (flags & FLAGS_LEFT) {
while (l++ < width) {
out(' ', args);
}
}
format++;
break;
}
case 'p': {
width = sizeof(void *) * 2U;
flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;
#if defined(PRINTF_SUPPORT_LONG_LONG)
const bool is_ll = sizeof(uintptr_t) == sizeof(long long);
if (is_ll) {
idx = _ntoa_long_long(
out, args,
(uintptr_t)va_arg(va, void *), false,
16U, precision, width, flags);
} else {
#endif
idx = _ntoa_long(
out, args,
(unsigned long)((uintptr_t)va_arg(
va, void *)),
false, 16U, precision, width, flags);
#if defined(PRINTF_SUPPORT_LONG_LONG)
}
#endif
format++;
break;
}
case '%':
set_format_ch(args);
out('%', args);
unset_format_ch(args);
format++;
break;
default:
set_format_ch(args);
out(*format, args);
unset_format_ch(args);
format++;
break;
}
}
// termination
out((char)0, args);
// return written chars without terminating \0
return (int)idx;
}
///////////////////////////////////////////////////////////////////////////////
static void out_tty(char c, struct out_tty_args *args)
{
if (c == 0) {
return;
}
enum b_tty_print_flags flags = args->flags;
if (!args->format_ch && (flags & B_TTY_DISABLE_INTERPOLATED_FORMATTING)) {
flags |= B_TTY_DISABLE_FORMATTING;
}
b_tty_putc(args->tty, flags, c);
}
int b_tty_vprintf(struct b_tty *tty, enum b_tty_print_flags flags, const char *format, va_list args)
{
struct out_tty_args tty_args = {
.flags = flags,
.tty = tty,
};
const int ret = _vsnprintf(
out_tty, &tty_args, format, args);
return ret;
}

51
term/printf.h Normal file
View File

@@ -0,0 +1,51 @@
///////////////////////////////////////////////////////////////////////////////
// \author (c) Marco Paland (info@paland.com)
// 2014-2019, PALANDesign Hannover, Germany
//
// \license The MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on
// embedded systems with a very limited resources.
// Use this instead of bloated standard/newlib printf.
// These routines are thread safe and reentrant.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef _PRINTF_H_
#define _PRINTF_H_
#include <stdarg.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif // _PRINTF_H_

461
term/sys/darwin/tty.c Normal file
View File

@@ -0,0 +1,461 @@
#include "../../../line-ed/tty.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#define ANSI_BOLD_ON "1"
#define ANSI_BOLD_OFF "22"
#define ANSI_ITALIC_ON "3"
#define ANSI_ITALIC_OFF "23"
#define ANSI_UNDERLINE_ON "4"
#define ANSI_UNDERLINE_OFF "24"
#define ANSI_DEFAULTCOLOUR_FG "39"
#define ANSI_DEFAULTCOLOUR_BG "49"
#define ANSI_256COLOUR_FG "38;5"
#define ANSI_256COLOUR_BG "48;5"
#define ANSI_TRUECOLOUR_FG "38;2"
#define ANSI_TRUECOLOUR_BG "48;2"
enum tty_flags {
TTY_INIT = 0x01u,
TTY_INTERACTIVE = 0x02u,
};
struct s_tty {
FILE *t_in, *t_out;
enum tty_flags t_flags;
struct termios t_ios_raw, t_ios_default;
struct s_tty_vmode t_vmode;
unsigned char t_mcount;
};
static struct s_tty tty = {};
static const char *ansi_colour16_fg[] = {
[TTY_COLOUR16_BLACK] = "30",
[TTY_COLOUR16_RED] = "31",
[TTY_COLOUR16_GREEN] = "32",
[TTY_COLOUR16_YELLOW] = "33",
[TTY_COLOUR16_BLUE] = "34",
[TTY_COLOUR16_MAGENTA] = "35",
[TTY_COLOUR16_CYAN] = "36",
[TTY_COLOUR16_WHITE] = "37",
[TTY_COLOUR16_GREY] = "90",
[TTY_COLOUR16_BRIGHT_RED] = "91",
[TTY_COLOUR16_BRIGHT_GREEN] = "92",
[TTY_COLOUR16_BRIGHT_YELLOW] = "93",
[TTY_COLOUR16_BRIGHT_BLUE] = "94",
[TTY_COLOUR16_BRIGHT_MAGENTA] = "95",
[TTY_COLOUR16_BRIGHT_CYAN] = "96",
[TTY_COLOUR16_BRIGHT_WHITE] = "97",
};
static const char *ansi_colour16_bg[] = {
[TTY_COLOUR16_BLACK] = "40",
[TTY_COLOUR16_RED] = "41",
[TTY_COLOUR16_GREEN] = "42",
[TTY_COLOUR16_YELLOW] = "43",
[TTY_COLOUR16_BLUE] = "44",
[TTY_COLOUR16_MAGENTA] = "45",
[TTY_COLOUR16_CYAN] = "46",
[TTY_COLOUR16_WHITE] = "47",
[TTY_COLOUR16_GREY] = "100",
[TTY_COLOUR16_BRIGHT_RED] = "101",
[TTY_COLOUR16_BRIGHT_GREEN] = "102",
[TTY_COLOUR16_BRIGHT_YELLOW] = "103",
[TTY_COLOUR16_BRIGHT_BLUE] = "104",
[TTY_COLOUR16_BRIGHT_MAGENTA] = "105",
[TTY_COLOUR16_BRIGHT_CYAN] = "106",
[TTY_COLOUR16_BRIGHT_WHITE] = "107",
};
static void init_tty(struct s_tty *tty, FILE *in, FILE *out)
{
tty->t_in = in;
tty->t_out = out;
int fd = fileno(in);
if (isatty(fd)) {
tty->t_flags |= TTY_INTERACTIVE;
}
tcgetattr(fd, &tty->t_ios_default);
memcpy(&tty->t_ios_raw, &tty->t_ios_default, sizeof tty->t_ios_raw);
tty->t_ios_raw.c_iflag &= ~(ICRNL | IXON);
tty->t_ios_raw.c_oflag &= ~(OPOST);
tty->t_ios_raw.c_lflag &= ~(ECHO | ICANON | IEXTEN);
tty->t_flags |= TTY_INIT;
}
struct s_tty *s_get_tty(void)
{
if (!(tty.t_flags & TTY_INIT)) {
init_tty(&tty, stdin, stdout);
}
return &tty;
}
bool s_tty_is_interactive(const struct s_tty *tty)
{
return (tty->t_flags & TTY_INTERACTIVE) == TTY_INTERACTIVE;
}
void s_tty_set_raw(struct s_tty *tty)
{
tcsetattr(STDIN_FILENO, TCSAFLUSH, &tty->t_ios_raw);
}
void s_tty_set_canon(struct s_tty *tty)
{
tcsetattr(STDIN_FILENO, TCSAFLUSH, &tty->t_ios_default);
}
void s_tty_reset_vmode(struct s_tty *tty)
{
if (tty->t_vmode.v_fg.c_mode == TTY_COLOUR_NONE
&& tty->t_vmode.v_bg.c_mode == TTY_COLOUR_NONE
&& tty->t_vmode.v_attrib == TTY_ATTRIB_NORMAL) {
return;
}
fprintf(tty->t_out, "\033[0;39;49m");
memset(&tty->t_vmode, 0x0, sizeof tty->t_vmode);
tty->t_vmode.v_fg.c_mode = TTY_COLOUR_NONE;
tty->t_vmode.v_bg.c_mode = TTY_COLOUR_NONE;
tty->t_vmode.v_attrib = TTY_ATTRIB_NORMAL;
}
static int compare_colour(
const struct s_tty_colour *a, const struct s_tty_colour *b)
{
if (a->c_mode != b->c_mode) {
return -1;
}
switch (a->c_mode) {
case TTY_COLOUR_16:
if (a->c_16.value != b->c_16.value) {
return -1;
}
break;
case TTY_COLOUR_256:
if (a->c_256.value != b->c_256.value) {
return -1;
}
break;
case TTY_COLOUR_TRUE:
if (a->c_true.r != b->c_true.r) {
return -1;
}
if (a->c_true.g != b->c_true.g) {
return -1;
}
if (a->c_true.b != b->c_true.b) {
return -1;
}
break;
default:
break;
}
return 0;
}
static int compare_vmode(const struct s_tty_vmode *a, const struct s_tty_vmode *b)
{
if (a->v_attrib != b->v_attrib) {
return -1;
}
if (compare_colour(&a->v_fg, &b->v_fg) != 0) {
return -1;
}
if (compare_colour(&a->v_bg, &b->v_bg) != 0) {
return -1;
}
return 0;
}
static void put_ansi_attrib(struct s_tty *tty, const char *s)
{
if (tty->t_mcount > 0) {
fputs(";", tty->t_out);
}
fputs(s, tty->t_out);
tty->t_mcount++;
}
static void set_attrib(
struct s_tty *tty, enum s_tty_attrib old, enum s_tty_attrib new)
{
if (old == new) {
return;
}
/* Bold ON */
if (!(old & TTY_ATTRIB_BOLD) && new &TTY_ATTRIB_BOLD) {
put_ansi_attrib(tty, ANSI_BOLD_ON);
}
/* Bold OFF */
if (old & TTY_ATTRIB_BOLD && !(new &TTY_ATTRIB_BOLD)) {
put_ansi_attrib(tty, ANSI_BOLD_OFF);
}
/* Underline ON */
if (!(old & TTY_ATTRIB_UNDERLINE) && new &TTY_ATTRIB_UNDERLINE) {
put_ansi_attrib(tty, ANSI_UNDERLINE_ON);
}
/* Underline OFF */
if (old & TTY_ATTRIB_UNDERLINE && !(new &TTY_ATTRIB_UNDERLINE)) {
put_ansi_attrib(tty, ANSI_UNDERLINE_OFF);
}
/* Italic ON */
if (!(old & TTY_ATTRIB_ITALIC) && new &TTY_ATTRIB_ITALIC) {
put_ansi_attrib(tty, ANSI_ITALIC_ON);
}
/* Italic OFF */
if (old & TTY_ATTRIB_ITALIC && !(new &TTY_ATTRIB_ITALIC)) {
put_ansi_attrib(tty, ANSI_ITALIC_OFF);
}
}
static void set_fg(
struct s_tty *tty, const struct s_tty_colour *old,
const struct s_tty_colour *new)
{
if (compare_colour(old, new) == 0) {
return;
}
char buf[8];
switch (new->c_mode) {
case TTY_COLOUR_NONE:
put_ansi_attrib(tty, ANSI_DEFAULTCOLOUR_FG);
break;
case TTY_COLOUR_16:
put_ansi_attrib(tty, ansi_colour16_fg[new->c_16.value]);
break;
case TTY_COLOUR_256:
put_ansi_attrib(tty, ANSI_256COLOUR_FG);
snprintf(buf, sizeof buf, "%u", new->c_256.value);
put_ansi_attrib(tty, buf);
break;
case TTY_COLOUR_TRUE:
put_ansi_attrib(tty, ANSI_TRUECOLOUR_FG);
snprintf(buf, sizeof buf, "%u", new->c_true.r);
put_ansi_attrib(tty, buf);
snprintf(buf, sizeof buf, "%u", new->c_true.g);
put_ansi_attrib(tty, buf);
snprintf(buf, sizeof buf, "%u", new->c_true.b);
put_ansi_attrib(tty, buf);
break;
default:
break;
}
}
static void set_bg(
struct s_tty *tty, const struct s_tty_colour *old,
const struct s_tty_colour *new)
{
if (compare_colour(old, new) == 0) {
return;
}
char buf[8];
switch (new->c_mode) {
case TTY_COLOUR_NONE:
put_ansi_attrib(tty, ANSI_DEFAULTCOLOUR_BG);
break;
case TTY_COLOUR_16:
put_ansi_attrib(tty, ansi_colour16_bg[new->c_16.value]);
break;
case TTY_COLOUR_256:
put_ansi_attrib(tty, ANSI_256COLOUR_BG);
snprintf(buf, sizeof buf, "%u", new->c_256.value);
put_ansi_attrib(tty, buf);
break;
case TTY_COLOUR_TRUE:
put_ansi_attrib(tty, ANSI_TRUECOLOUR_BG);
snprintf(buf, sizeof buf, "%u", new->c_true.r);
put_ansi_attrib(tty, buf);
snprintf(buf, sizeof buf, "%u", new->c_true.g);
put_ansi_attrib(tty, buf);
snprintf(buf, sizeof buf, "%u", new->c_true.b);
put_ansi_attrib(tty, buf);
break;
default:
break;
}
}
void s_tty_set_vmode(struct s_tty *tty, const struct s_tty_vmode *vmode)
{
if (compare_vmode(&tty->t_vmode, vmode) == 0) {
return;
}
tty->t_mcount = 0;
fprintf(tty->t_out, "\033[");
set_attrib(tty, tty->t_vmode.v_attrib, vmode->v_attrib);
set_fg(tty, &tty->t_vmode.v_fg, &vmode->v_fg);
set_bg(tty, &tty->t_vmode.v_bg, &vmode->v_bg);
fprintf(tty->t_out, "m");
memcpy(&tty->t_vmode, vmode, sizeof *vmode);
}
s_keycode s_tty_read_key(struct s_tty *tty)
{
char c;
int v;
int fd = fileno(tty->t_in);
while (1) {
v = read(fd, &c, 1);
if (v < 1) {
return S_KEY_EOF;
}
if (c == '\r' || c == '\n') {
return S_KEY_RETURN;
}
if (c == '\b' || c == 0x7F) {
return S_KEY_BACKSPACE;
}
if (c >= 1 && c <= 26) {
return S_TTY_CTRL_KEY(c + 'a' - 1);
}
if (c != 0x1b) {
return c;
}
v = read(fd, &c, 1);
if (v < 1) {
return S_KEY_EOF;
}
if (c != '[') {
continue;
}
v = read(fd, &c, 1);
if (v < 1) {
return S_KEY_EOF;
}
switch (c) {
case 'A':
return S_KEY_ARROW_UP;
case 'B':
return S_KEY_ARROW_DOWN;
case 'C':
return S_KEY_ARROW_RIGHT;
case 'D':
return S_KEY_ARROW_LEFT;
default:
continue;
}
}
return c;
}
void s_tty_move_cursor_x(struct s_tty *tty, enum s_tty_position_base base, int pos)
{
if (base == TTY_POS_CURSOR && pos < 0 && pos >= -4) {
for (int i = 0; i > pos; i--) {
fputc('\b', tty->t_out);
}
return;
}
if (base == TTY_POS_START) {
if (pos == 0) {
fputs("\033[G", tty->t_out);
} else {
fprintf(tty->t_out, "\033[%dG", pos + 1);
}
} else {
if (pos > 1) {
fprintf(tty->t_out, "\033[%dC", pos);
} else if (pos == 1) {
fputs("\033[C", tty->t_out);
} else if (pos == -1) {
fputs("\033[D", tty->t_out);
} else if (pos < -1) {
fprintf(tty->t_out, "\033[%dD", -pos);
}
}
}
void s_tty_move_cursor_y(struct s_tty *tty, enum s_tty_position_base base, int pos)
{
if (base == TTY_POS_START) {
/* we don't need this functionality right now */
abort();
}
if (pos > 1) {
fprintf(tty->t_out, "\033[%dB", pos);
} else if (pos == 1) {
fputs("\033[B", tty->t_out);
} else if (pos == -1) {
fputs("\033[A", tty->t_out);
} else if (pos < -1) {
fprintf(tty->t_out, "\033[%dA", -pos);
}
}
void s_tty_clear(struct s_tty *tty, enum s_tty_clear_mode mode)
{
const char *arg;
if (mode & TTY_CLEAR_ALL) {
arg = "2";
} else if (mode & TTY_CLEAR_TO_CURSOR) {
arg = "1";
} else if (mode & TTY_CLEAR_FROM_CURSOR) {
arg = "";
} else {
abort();
}
if (mode & TTY_CLEAR_SCREEN) {
fprintf(tty->t_out, "\033[%sJ", arg);
} else if (mode & TTY_CLEAR_LINE) {
fprintf(tty->t_out, "\033[%sK", arg);
} else {
abort();
}
}

461
term/sys/linux/tty.c Normal file
View File

@@ -0,0 +1,461 @@
#include "../../../line-ed/tty.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#define ANSI_BOLD_ON "1"
#define ANSI_BOLD_OFF "22"
#define ANSI_ITALIC_ON "3"
#define ANSI_ITALIC_OFF "23"
#define ANSI_UNDERLINE_ON "4"
#define ANSI_UNDERLINE_OFF "24"
#define ANSI_DEFAULTCOLOUR_FG "39"
#define ANSI_DEFAULTCOLOUR_BG "49"
#define ANSI_256COLOUR_FG "38;5"
#define ANSI_256COLOUR_BG "48;5"
#define ANSI_TRUECOLOUR_FG "38;2"
#define ANSI_TRUECOLOUR_BG "48;2"
enum tty_flags {
TTY_INIT = 0x01u,
TTY_INTERACTIVE = 0x02u,
};
struct s_tty {
FILE *t_in, *t_out;
enum tty_flags t_flags;
struct termios t_ios_raw, t_ios_default;
struct s_tty_vmode t_vmode;
unsigned char t_mcount;
};
static struct s_tty tty = {};
static const char *ansi_colour16_fg[] = {
[TTY_COLOUR16_BLACK] = "30",
[TTY_COLOUR16_RED] = "31",
[TTY_COLOUR16_GREEN] = "32",
[TTY_COLOUR16_YELLOW] = "33",
[TTY_COLOUR16_BLUE] = "34",
[TTY_COLOUR16_MAGENTA] = "35",
[TTY_COLOUR16_CYAN] = "36",
[TTY_COLOUR16_WHITE] = "37",
[TTY_COLOUR16_GREY] = "90",
[TTY_COLOUR16_BRIGHT_RED] = "91",
[TTY_COLOUR16_BRIGHT_GREEN] = "92",
[TTY_COLOUR16_BRIGHT_YELLOW] = "93",
[TTY_COLOUR16_BRIGHT_BLUE] = "94",
[TTY_COLOUR16_BRIGHT_MAGENTA] = "95",
[TTY_COLOUR16_BRIGHT_CYAN] = "96",
[TTY_COLOUR16_BRIGHT_WHITE] = "97",
};
static const char *ansi_colour16_bg[] = {
[TTY_COLOUR16_BLACK] = "40",
[TTY_COLOUR16_RED] = "41",
[TTY_COLOUR16_GREEN] = "42",
[TTY_COLOUR16_YELLOW] = "43",
[TTY_COLOUR16_BLUE] = "44",
[TTY_COLOUR16_MAGENTA] = "45",
[TTY_COLOUR16_CYAN] = "46",
[TTY_COLOUR16_WHITE] = "47",
[TTY_COLOUR16_GREY] = "100",
[TTY_COLOUR16_BRIGHT_RED] = "101",
[TTY_COLOUR16_BRIGHT_GREEN] = "102",
[TTY_COLOUR16_BRIGHT_YELLOW] = "103",
[TTY_COLOUR16_BRIGHT_BLUE] = "104",
[TTY_COLOUR16_BRIGHT_MAGENTA] = "105",
[TTY_COLOUR16_BRIGHT_CYAN] = "106",
[TTY_COLOUR16_BRIGHT_WHITE] = "107",
};
static void init_tty(struct s_tty *tty, FILE *in, FILE *out)
{
tty->t_in = in;
tty->t_out = out;
int fd = fileno(in);
if (isatty(fd)) {
tty->t_flags |= TTY_INTERACTIVE;
}
tcgetattr(fd, &tty->t_ios_default);
memcpy(&tty->t_ios_raw, &tty->t_ios_default, sizeof tty->t_ios_raw);
tty->t_ios_raw.c_iflag &= ~(ICRNL | IXON);
tty->t_ios_raw.c_oflag &= ~(OPOST);
tty->t_ios_raw.c_lflag &= ~(ECHO | ICANON | IEXTEN);
tty->t_flags |= TTY_INIT;
}
struct s_tty *s_get_tty(void)
{
if (!(tty.t_flags & TTY_INIT)) {
init_tty(&tty, stdin, stdout);
}
return &tty;
}
bool s_tty_is_interactive(const struct s_tty *tty)
{
return (tty->t_flags & TTY_INTERACTIVE) == TTY_INTERACTIVE;
}
void s_tty_set_raw(struct s_tty *tty)
{
tcsetattr(STDIN_FILENO, TCSAFLUSH, &tty->t_ios_raw);
}
void s_tty_set_canon(struct s_tty *tty)
{
tcsetattr(STDIN_FILENO, TCSAFLUSH, &tty->t_ios_default);
}
void s_tty_reset_vmode(struct s_tty *tty)
{
if (tty->t_vmode.v_fg.c_mode == TTY_COLOUR_NONE
&& tty->t_vmode.v_bg.c_mode == TTY_COLOUR_NONE
&& tty->t_vmode.v_attrib == TTY_ATTRIB_NORMAL) {
return;
}
fprintf(tty->t_out, "\033[0;39;49m");
memset(&tty->t_vmode, 0x0, sizeof tty->t_vmode);
tty->t_vmode.v_fg.c_mode = TTY_COLOUR_NONE;
tty->t_vmode.v_bg.c_mode = TTY_COLOUR_NONE;
tty->t_vmode.v_attrib = TTY_ATTRIB_NORMAL;
}
static int compare_colour(
const struct s_tty_colour *a, const struct s_tty_colour *b)
{
if (a->c_mode != b->c_mode) {
return -1;
}
switch (a->c_mode) {
case TTY_COLOUR_16:
if (a->c_16.value != b->c_16.value) {
return -1;
}
break;
case TTY_COLOUR_256:
if (a->c_256.value != b->c_256.value) {
return -1;
}
break;
case TTY_COLOUR_TRUE:
if (a->c_true.r != b->c_true.r) {
return -1;
}
if (a->c_true.g != b->c_true.g) {
return -1;
}
if (a->c_true.b != b->c_true.b) {
return -1;
}
break;
default:
break;
}
return 0;
}
static int compare_vmode(const struct s_tty_vmode *a, const struct s_tty_vmode *b)
{
if (a->v_attrib != b->v_attrib) {
return -1;
}
if (compare_colour(&a->v_fg, &b->v_fg) != 0) {
return -1;
}
if (compare_colour(&a->v_bg, &b->v_bg) != 0) {
return -1;
}
return 0;
}
static void put_ansi_attrib(struct s_tty *tty, const char *s)
{
if (tty->t_mcount > 0) {
fputs(";", tty->t_out);
}
fputs(s, tty->t_out);
tty->t_mcount++;
}
static void set_attrib(
struct s_tty *tty, enum s_tty_attrib old, enum s_tty_attrib new)
{
if (old == new) {
return;
}
/* Bold ON */
if (!(old & TTY_ATTRIB_BOLD) && new &TTY_ATTRIB_BOLD) {
put_ansi_attrib(tty, ANSI_BOLD_ON);
}
/* Bold OFF */
if (old & TTY_ATTRIB_BOLD && !(new &TTY_ATTRIB_BOLD)) {
put_ansi_attrib(tty, ANSI_BOLD_OFF);
}
/* Underline ON */
if (!(old & TTY_ATTRIB_UNDERLINE) && new &TTY_ATTRIB_UNDERLINE) {
put_ansi_attrib(tty, ANSI_UNDERLINE_ON);
}
/* Underline OFF */
if (old & TTY_ATTRIB_UNDERLINE && !(new &TTY_ATTRIB_UNDERLINE)) {
put_ansi_attrib(tty, ANSI_UNDERLINE_OFF);
}
/* Italic ON */
if (!(old & TTY_ATTRIB_ITALIC) && new &TTY_ATTRIB_ITALIC) {
put_ansi_attrib(tty, ANSI_ITALIC_ON);
}
/* Italic OFF */
if (old & TTY_ATTRIB_ITALIC && !(new &TTY_ATTRIB_ITALIC)) {
put_ansi_attrib(tty, ANSI_ITALIC_OFF);
}
}
static void set_fg(
struct s_tty *tty, const struct s_tty_colour *old,
const struct s_tty_colour *new)
{
if (compare_colour(old, new) == 0) {
return;
}
char buf[8];
switch (new->c_mode) {
case TTY_COLOUR_NONE:
put_ansi_attrib(tty, ANSI_DEFAULTCOLOUR_FG);
break;
case TTY_COLOUR_16:
put_ansi_attrib(tty, ansi_colour16_fg[new->c_16.value]);
break;
case TTY_COLOUR_256:
put_ansi_attrib(tty, ANSI_256COLOUR_FG);
snprintf(buf, sizeof buf, "%u", new->c_256.value);
put_ansi_attrib(tty, buf);
break;
case TTY_COLOUR_TRUE:
put_ansi_attrib(tty, ANSI_TRUECOLOUR_FG);
snprintf(buf, sizeof buf, "%u", new->c_true.r);
put_ansi_attrib(tty, buf);
snprintf(buf, sizeof buf, "%u", new->c_true.g);
put_ansi_attrib(tty, buf);
snprintf(buf, sizeof buf, "%u", new->c_true.b);
put_ansi_attrib(tty, buf);
break;
default:
break;
}
}
static void set_bg(
struct s_tty *tty, const struct s_tty_colour *old,
const struct s_tty_colour *new)
{
if (compare_colour(old, new) == 0) {
return;
}
char buf[8];
switch (new->c_mode) {
case TTY_COLOUR_NONE:
put_ansi_attrib(tty, ANSI_DEFAULTCOLOUR_BG);
break;
case TTY_COLOUR_16:
put_ansi_attrib(tty, ansi_colour16_bg[new->c_16.value]);
break;
case TTY_COLOUR_256:
put_ansi_attrib(tty, ANSI_256COLOUR_BG);
snprintf(buf, sizeof buf, "%u", new->c_256.value);
put_ansi_attrib(tty, buf);
break;
case TTY_COLOUR_TRUE:
put_ansi_attrib(tty, ANSI_TRUECOLOUR_BG);
snprintf(buf, sizeof buf, "%u", new->c_true.r);
put_ansi_attrib(tty, buf);
snprintf(buf, sizeof buf, "%u", new->c_true.g);
put_ansi_attrib(tty, buf);
snprintf(buf, sizeof buf, "%u", new->c_true.b);
put_ansi_attrib(tty, buf);
break;
default:
break;
}
}
void s_tty_set_vmode(struct s_tty *tty, const struct s_tty_vmode *vmode)
{
if (compare_vmode(&tty->t_vmode, vmode) == 0) {
return;
}
tty->t_mcount = 0;
fprintf(tty->t_out, "\033[");
set_attrib(tty, tty->t_vmode.v_attrib, vmode->v_attrib);
set_fg(tty, &tty->t_vmode.v_fg, &vmode->v_fg);
set_bg(tty, &tty->t_vmode.v_bg, &vmode->v_bg);
fprintf(tty->t_out, "m");
memcpy(&tty->t_vmode, vmode, sizeof *vmode);
}
s_keycode s_tty_read_key(struct s_tty *tty)
{
char c;
int v;
int fd = fileno(tty->t_in);
while (1) {
v = read(fd, &c, 1);
if (v < 1) {
return S_KEY_EOF;
}
if (c == '\r' || c == '\n') {
return S_KEY_RETURN;
}
if (c == '\b' || c == 0x7F) {
return S_KEY_BACKSPACE;
}
if (c >= 1 && c <= 26) {
return S_TTY_CTRL_KEY(c + 'a' - 1);
}
if (c != 0x1b) {
return c;
}
v = read(fd, &c, 1);
if (v < 1) {
return S_KEY_EOF;
}
if (c != '[') {
continue;
}
v = read(fd, &c, 1);
if (v < 1) {
return S_KEY_EOF;
}
switch (c) {
case 'A':
return S_KEY_ARROW_UP;
case 'B':
return S_KEY_ARROW_DOWN;
case 'C':
return S_KEY_ARROW_RIGHT;
case 'D':
return S_KEY_ARROW_LEFT;
default:
continue;
}
}
return c;
}
void s_tty_move_cursor_x(struct s_tty *tty, enum s_tty_position_base base, int pos)
{
if (base == TTY_POS_CURSOR && pos < 0 && pos >= -4) {
for (int i = 0; i > pos; i--) {
fputc('\b', tty->t_out);
}
return;
}
if (base == TTY_POS_START) {
if (pos == 0) {
fputs("\033[G", tty->t_out);
} else {
fprintf(tty->t_out, "\033[%dG", pos + 1);
}
} else {
if (pos > 1) {
fprintf(tty->t_out, "\033[%dC", pos);
} else if (pos == 1) {
fputs("\033[C", tty->t_out);
} else if (pos == -1) {
fputs("\033[D", tty->t_out);
} else if (pos < -1) {
fprintf(tty->t_out, "\033[%dD", -pos);
}
}
}
void s_tty_move_cursor_y(struct s_tty *tty, enum s_tty_position_base base, int pos)
{
if (base == TTY_POS_START) {
/* we don't need this functionality right now */
abort();
}
if (pos > 1) {
fprintf(tty->t_out, "\033[%dB", pos);
} else if (pos == 1) {
fputs("\033[B", tty->t_out);
} else if (pos == -1) {
fputs("\033[A", tty->t_out);
} else if (pos < -1) {
fprintf(tty->t_out, "\033[%dA", -pos);
}
}
void s_tty_clear(struct s_tty *tty, enum s_tty_clear_mode mode)
{
const char *arg;
if (mode & TTY_CLEAR_ALL) {
arg = "2";
} else if (mode & TTY_CLEAR_TO_CURSOR) {
arg = "1";
} else if (mode & TTY_CLEAR_FROM_CURSOR) {
arg = "";
} else {
abort();
}
if (mode & TTY_CLEAR_SCREEN) {
fprintf(tty->t_out, "\033[%sJ", arg);
} else if (mode & TTY_CLEAR_LINE) {
fprintf(tty->t_out, "\033[%sK", arg);
} else {
abort();
}
}

570
term/sys/windows/tty.c Normal file
View File

@@ -0,0 +1,570 @@
#include <blue/term/tty.h>
#include "../../tty.h"
#include <stdio.h>
#include <string.h>
#include <Windows.h>
#include <io.h>
#include <stdbool.h>
#define ANSI_BOLD_ON "1"
#define ANSI_BOLD_OFF "22"
#define ANSI_ITALIC_ON "3"
#define ANSI_ITALIC_OFF "23"
#define ANSI_UNDERLINE_ON "4"
#define ANSI_UNDERLINE_OFF "24"
#define ANSI_DEFAULTCOLOUR_FG "39"
#define ANSI_DEFAULTCOLOUR_BG "49"
#define ANSI_256COLOUR_FG "38;5"
#define ANSI_256COLOUR_BG "48;5"
#define ANSI_TRUECOLOUR_FG "38;2"
#define ANSI_TRUECOLOUR_BG "48;2"
enum tty_flags {
B_TTY_INIT = 0x01u,
B_TTY_INTERACTIVE = 0x02u,
};
struct b_tty {
HANDLE t_in, t_out;
DWORD t_canon_mode_in, t_canon_mode_out;
enum s_key_code t_repeat_key;
unsigned int t_repeat_count;
enum tty_flags t_flags;
struct b_tty_vmode t_vmode;
struct tty_format_buf t_format_buf;
};
static struct b_tty std = {0};
static struct b_tty err = {0};
static WORD ansi_colour16_fg[] = {
[B_TTY_COLOUR16_BLACK] = 0,
[B_TTY_COLOUR16_RED] = FOREGROUND_RED,
[B_TTY_COLOUR16_GREEN] = FOREGROUND_GREEN,
[B_TTY_COLOUR16_YELLOW] = FOREGROUND_RED | FOREGROUND_GREEN,
[B_TTY_COLOUR16_BLUE] = FOREGROUND_BLUE,
[B_TTY_COLOUR16_MAGENTA] = FOREGROUND_RED | FOREGROUND_BLUE,
[B_TTY_COLOUR16_CYAN] = FOREGROUND_GREEN | FOREGROUND_BLUE,
[B_TTY_COLOUR16_WHITE] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
[B_TTY_COLOUR16_BRIGHT_BLACK] = FOREGROUND_INTENSITY,
[B_TTY_COLOUR16_BRIGHT_RED] = FOREGROUND_RED | FOREGROUND_INTENSITY,
[B_TTY_COLOUR16_BRIGHT_GREEN] = FOREGROUND_GREEN | FOREGROUND_INTENSITY,
[B_TTY_COLOUR16_BRIGHT_YELLOW] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY,
[B_TTY_COLOUR16_BRIGHT_BLUE] = FOREGROUND_BLUE | FOREGROUND_INTENSITY,
[B_TTY_COLOUR16_BRIGHT_MAGENTA] = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
[B_TTY_COLOUR16_BRIGHT_CYAN] = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
[B_TTY_COLOUR16_BRIGHT_WHITE] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
};
static WORD ansi_colour16_bg[] = {
[B_TTY_COLOUR16_BLACK] = 0,
[B_TTY_COLOUR16_RED] = BACKGROUND_RED,
[B_TTY_COLOUR16_GREEN] = BACKGROUND_GREEN,
[B_TTY_COLOUR16_YELLOW] = BACKGROUND_RED | BACKGROUND_GREEN,
[B_TTY_COLOUR16_BLUE] = BACKGROUND_BLUE,
[B_TTY_COLOUR16_MAGENTA] = BACKGROUND_RED | BACKGROUND_BLUE,
[B_TTY_COLOUR16_CYAN] = BACKGROUND_GREEN | BACKGROUND_BLUE,
[B_TTY_COLOUR16_WHITE] = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE,
[B_TTY_COLOUR16_BRIGHT_BLACK] = BACKGROUND_INTENSITY,
[B_TTY_COLOUR16_BRIGHT_RED] = BACKGROUND_RED | BACKGROUND_INTENSITY,
[B_TTY_COLOUR16_BRIGHT_GREEN] = BACKGROUND_GREEN | BACKGROUND_INTENSITY,
[B_TTY_COLOUR16_BRIGHT_YELLOW] = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_INTENSITY,
[B_TTY_COLOUR16_BRIGHT_BLUE] = BACKGROUND_BLUE | BACKGROUND_INTENSITY,
[B_TTY_COLOUR16_BRIGHT_MAGENTA] = BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY,
[B_TTY_COLOUR16_BRIGHT_CYAN] = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY,
[B_TTY_COLOUR16_BRIGHT_WHITE] = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY,
};
static void init_tty(struct b_tty *tty, FILE *in, FILE *out)
{
HANDLE in_handle = INVALID_HANDLE_VALUE;
HANDLE out_handle = INVALID_HANDLE_VALUE;
if (in) {
in_handle = (HANDLE)_get_osfhandle(fileno(in));
}
if (out) {
out_handle = (HANDLE)_get_osfhandle(fileno(out));
}
tty->t_in = in_handle;
tty->t_out = out_handle;
tty->t_flags |= B_TTY_INIT;
CONSOLE_SCREEN_BUFFER_INFO csbi = {0};
DWORD mode = 0;
if (in && GetConsoleScreenBufferInfo(in_handle, &csbi)) {
GetConsoleMode(in_handle, &mode);
tty->t_canon_mode_in = mode;
tty->t_flags |= B_TTY_INTERACTIVE;
}
if (out && GetConsoleScreenBufferInfo(out_handle, &csbi)) {
GetConsoleMode(out_handle, &mode);
tty->t_canon_mode_out = mode;
tty->t_flags |= B_TTY_INTERACTIVE;
}
}
struct b_tty *z__b_tty_get_std(void)
{
if (!(std.t_flags & B_TTY_INIT)) {
init_tty(&std, stdin, stdout);
}
return &std;
}
struct b_tty *z__b_tty_get_err(void)
{
if (!(err.t_flags & B_TTY_INIT)) {
init_tty(&err, NULL, stderr);
}
return &err;
}
struct tty_format_buf *z__b_tty_get_format_buf(struct b_tty *tty)
{
return &tty->t_format_buf;
}
void z__b_tty_putc(struct b_tty* tty, char c)
{
DWORD x;
WriteConsoleA(tty->t_out, &c, 1, &x, NULL);
}
bool b_tty_is_interactive(const struct b_tty *tty)
{
return (tty->t_flags & B_TTY_INTERACTIVE) == B_TTY_INTERACTIVE;
}
static void tty_set_raw(struct b_tty *tty)
{
DWORD mode = tty->t_canon_mode_in;
mode &= ~(ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | ENABLE_QUICK_EDIT_MODE | ENABLE_WINDOW_INPUT);
mode |= (ENABLE_PROCESSED_INPUT);
SetConsoleMode(tty->t_in, mode);
mode = tty->t_canon_mode_out;
//mode &= ~(ENABLE_PROCESSED_OUTPUT);
SetConsoleMode(tty->t_out, mode);
}
static void tty_set_canon(struct b_tty *tty)
{
SetConsoleMode(tty->t_in, tty->t_canon_mode_in);
SetConsoleMode(tty->t_out, tty->t_canon_mode_out);
}
void b_tty_set_mode(struct b_tty* tty, enum b_tty_mode mode)
{
switch (mode) {
case B_TTY_CANONICAL:
tty_set_canon(tty);
break;
case B_TTY_RAW:
tty_set_raw(tty);
break;
default:
break;
}
}
void b_tty_reset_vmode(struct b_tty *tty)
{
if (tty->t_vmode.v_fg.c_mode == B_TTY_COLOUR_NONE
&& tty->t_vmode.v_bg.c_mode == B_TTY_COLOUR_NONE
&& tty->t_vmode.v_attrib == B_TTY_ATTRIB_NORMAL) {
return;
}
WORD attrib = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
SetConsoleTextAttribute(tty->t_out, attrib);
memset(&tty->t_vmode, 0x0, sizeof tty->t_vmode);
tty->t_vmode.v_fg.c_mode = B_TTY_COLOUR_NONE;
tty->t_vmode.v_bg.c_mode = B_TTY_COLOUR_NONE;
tty->t_vmode.v_attrib = B_TTY_ATTRIB_NORMAL;
}
static int compare_colour(
const struct b_tty_colour *a, const struct b_tty_colour *b)
{
if (a->c_mode != b->c_mode) {
return -1;
}
switch (a->c_mode) {
case B_TTY_COLOUR_16:
if (a->c_16.value != b->c_16.value) {
return -1;
}
break;
case B_TTY_COLOUR_256:
if (a->c_256.value != b->c_256.value) {
return -1;
}
break;
case B_TTY_COLOUR_TRUE:
if (a->c_true.r != b->c_true.r) {
return -1;
}
if (a->c_true.g != b->c_true.g) {
return -1;
}
if (a->c_true.b != b->c_true.b) {
return -1;
}
break;
default:
break;
}
return 0;
}
static int compare_vmode(const struct b_tty_vmode *a, const struct b_tty_vmode *b)
{
if (a->v_attrib != b->v_attrib) {
return -1;
}
if (compare_colour(&a->v_fg, &b->v_fg) != 0) {
return -1;
}
if (compare_colour(&a->v_bg, &b->v_bg) != 0) {
return -1;
}
return 0;
}
static void set_attrib(WORD *attrp, enum b_tty_attrib old, enum b_tty_attrib new)
{
if (old == new) {
return;
}
WORD attrib = *attrp;
/* Bold ON */
if (!(old & B_TTY_ATTRIB_BOLD) && new & B_TTY_ATTRIB_BOLD) {
attrib |= FOREGROUND_INTENSITY;
}
/* Bold OFF */
if (old & B_TTY_ATTRIB_BOLD && !(new & B_TTY_ATTRIB_BOLD)) {
attrib &= ~FOREGROUND_INTENSITY;
}
/* Underline ON */
if (!(old & B_TTY_ATTRIB_UNDERLINE) && new & B_TTY_ATTRIB_UNDERLINE) {
attrib |= COMMON_LVB_UNDERSCORE;
}
/* Underline OFF */
if (old & B_TTY_ATTRIB_UNDERLINE && !(new & B_TTY_ATTRIB_UNDERLINE)) {
attrib &= ~COMMON_LVB_UNDERSCORE;
}
/* Italic ON */
if (!(old & B_TTY_ATTRIB_ITALIC) && new & B_TTY_ATTRIB_ITALIC) {
/* not supported */
}
/* Italic OFF */
if (old & B_TTY_ATTRIB_ITALIC && !(new & B_TTY_ATTRIB_ITALIC)) {
/* not supported */
}
*attrp = attrib;
}
static void set_fg(
WORD *attrp, const struct b_tty_colour *old,
const struct b_tty_colour *new)
{
if (compare_colour(old, new) == 0) {
return;
}
WORD attrib = *attrp;
attrib &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
switch (new->c_mode) {
case B_TTY_COLOUR_16:
attrib |= ansi_colour16_fg[new->c_16.value];
break;
default:
attrib |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
break;
}
*attrp = attrib;
}
static void set_bg(
WORD *attrp, const struct b_tty_colour *old,
const struct b_tty_colour *new)
{
if (compare_colour(old, new) == 0) {
return;
}
WORD attrib = *attrp;
attrib &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY);
switch (new->c_mode) {
case B_TTY_COLOUR_16:
attrib |= ansi_colour16_bg[new->c_16.value];
break;
default:
attrib |= BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
break;
}
}
void b_tty_set_vmode(struct b_tty *tty, const struct b_tty_vmode *vmode)
{
if (compare_vmode(&tty->t_vmode, vmode) == 0) {
return;
}
WORD attrib = 0;
CONSOLE_SCREEN_BUFFER_INFO csbi = {0};
GetConsoleScreenBufferInfo(tty->t_out, &csbi);
attrib = csbi.wAttributes;
set_attrib(&attrib, tty->t_vmode.v_attrib, vmode->v_attrib);
set_fg(&attrib, &tty->t_vmode.v_fg, &vmode->v_fg);
set_bg(&attrib, &tty->t_vmode.v_bg, &vmode->v_bg);
SetConsoleTextAttribute(tty->t_out, attrib);
memcpy(&tty->t_vmode, vmode, sizeof *vmode);
}
enum b_status b_tty_get_dimensions(struct b_tty* tty, unsigned int* w, unsigned int* h)
{
if (!(tty->t_flags & B_TTY_INTERACTIVE)) {
return B_ERR_NOT_SUPPORTED;
}
CONSOLE_SCREEN_BUFFER_INFO csbi = {0};
GetConsoleScreenBufferInfo(tty->t_out, &csbi);
if (w) {
*w = csbi.srWindow.Right - csbi.srWindow.Left;
}
if (h) {
*h = csbi.srWindow.Bottom - csbi.srWindow.Top;
}
return B_SUCCESS;
}
enum b_status b_tty_get_cursor_position(struct b_tty *tty, unsigned int *x, unsigned int *y)
{
if (!(tty->t_flags & B_TTY_INTERACTIVE)) {
return B_ERR_NOT_SUPPORTED;
}
CONSOLE_SCREEN_BUFFER_INFO csbi = {0};
GetConsoleScreenBufferInfo(tty->t_out, &csbi);
if (x) {
*x = csbi.dwCursorPosition.X;
}
if (y) {
*y = csbi.dwCursorPosition.Y;
}
return B_SUCCESS;
}
b_keycode b_tty_read_key(struct b_tty *tty)
{
if (tty->t_repeat_count > 0) {
tty->t_repeat_count--;
return tty->t_repeat_key;
}
INPUT_RECORD d;
HANDLE in = tty->t_in;
BOOL status = TRUE;
CONSOLE_READCONSOLE_CONTROL ctrl = {0};
DWORD nr_read = 0;
while (1) {
status = ReadConsoleInputA(in, &d, 1, &nr_read);
if (status == FALSE) {
return B_KEY_EOF;
}
if (d.EventType != KEY_EVENT) {
continue;
}
if (!d.Event.KeyEvent.bKeyDown) {
continue;
}
b_keycode key = 0;
switch (d.Event.KeyEvent.wVirtualKeyCode) {
case VK_CONTROL:
case VK_RCONTROL:
case VK_SHIFT:
case VK_RSHIFT:
case VK_MENU:
continue;
case VK_UP:
key = B_KEY_ARROW_UP;
break;
case VK_DOWN:
key = B_KEY_ARROW_DOWN;
break;
case VK_LEFT:
key = B_KEY_ARROW_LEFT;
break;
case VK_RIGHT:
key = B_KEY_ARROW_RIGHT;
break;
case VK_BACK:
key = B_KEY_BACKSPACE;
break;
case VK_RETURN:
key = B_KEY_RETURN;
break;
default:
if (d.Event.KeyEvent.uChar.UnicodeChar == 0) {
continue;
}
key = d.Event.KeyEvent.uChar.AsciiChar;
break;
}
if (d.Event.KeyEvent.dwControlKeyState & LEFT_CTRL_PRESSED) {
key = B_TTY_CTRL_KEY('a' + key - 1);
}
if (d.Event.KeyEvent.wRepeatCount > 1) {
tty->t_repeat_count = d.Event.KeyEvent.wRepeatCount - 1;
tty->t_repeat_key = key;
}
return key;
}
return B_KEY_EOF;
}
void b_tty_move_cursor_x(
struct b_tty* tty, enum b_tty_position_base base, int pos)
{
CONSOLE_SCREEN_BUFFER_INFO console = {0};
GetConsoleScreenBufferInfo(tty->t_out, &console);
COORD cursor_pos;
cursor_pos.Y = console.dwCursorPosition.Y;
switch (base) {
case B_TTY_POS_CURSOR:
cursor_pos.X = console.dwCursorPosition.X + pos;
break;
case B_TTY_POS_START:
cursor_pos.X = pos;
break;
default:
break;
}
SetConsoleCursorPosition(tty->t_out, cursor_pos);
}
void b_tty_move_cursor_y(struct b_tty *tty, enum b_tty_position_base base, int pos)
{
CONSOLE_SCREEN_BUFFER_INFO console = {0};
GetConsoleScreenBufferInfo(tty->t_out, &console);
COORD cursor_pos;
cursor_pos.X = console.dwCursorPosition.X;
switch (base) {
case B_TTY_POS_CURSOR:
cursor_pos.Y = console.dwCursorPosition.Y + pos;
break;
case B_TTY_POS_START:
cursor_pos.Y = pos;
break;
default:
break;
}
SetConsoleCursorPosition(tty->t_out, cursor_pos);
}
void b_tty_clear(struct b_tty *tty, enum b_tty_clear_mode mode)
{
CONSOLE_SCREEN_BUFFER_INFO console = {0};
GetConsoleScreenBufferInfo(tty->t_out, &console);
WCHAR fill = L' ';
DWORD length = 0;
COORD start;
DWORD all_length = 0, line_length = 0;
if (mode & B_TTY_CLEAR_ALL) {
line_length = console.dwSize.X;
all_length = line_length * console.dwSize.Y;
} else if (mode & B_TTY_CLEAR_FROM_CURSOR) {
line_length = console.dwSize.X - console.dwCursorPosition.X + 1;
all_length = line_length + ((console.dwSize.Y - console.dwCursorPosition.Y) * console.dwSize.X);
} else if (mode & B_TTY_CLEAR_TO_CURSOR) {
line_length = console.dwCursorPosition.X;
all_length = line_length
+ ((console.dwCursorPosition.Y - 1) * console.dwSize.X);
} else {
abort();
}
if (mode & B_TTY_CLEAR_SCREEN) {
length = all_length;
if ((mode & B_TTY_CLEAR_ALL) || (mode & B_TTY_CLEAR_TO_CURSOR)) {
start.X = 0;
start.Y = 0;
} else if (mode & B_TTY_CLEAR_FROM_CURSOR) {
start = console.dwCursorPosition;
}
} else if (mode & B_TTY_CLEAR_LINE) {
length = line_length;
if ((mode & B_TTY_CLEAR_ALL) || (mode & B_TTY_CLEAR_TO_CURSOR)) {
start.X = 0;
start.Y = console.dwCursorPosition.Y;
} else if (mode & B_TTY_CLEAR_FROM_CURSOR) {
start = console.dwCursorPosition;
}
} else {
abort();
}
DWORD nr_written = 0;
FillConsoleOutputCharacterW(tty->t_out, fill, length, start, &nr_written);
}

330
term/tty.c Normal file
View File

@@ -0,0 +1,330 @@
#include <blue/term/tty.h>
#include <blue/core/hash.h>
#include "tty.h"
#include "printf.h"
#define MOD_HASH_BLACK 0x4b5dd0abbc6fc1e4
#define MOD_HASH_RED 0x89e9be1960f4c21c
#define MOD_HASH_GREEN 0x0f40f029637fecbc
#define MOD_HASH_YELLOW 0x8346a574925e75a9
#define MOD_HASH_BLUE 0xc5ccd29bc2dda64d
#define MOD_HASH_MAGENTA 0x6c90e772edbc8708
#define MOD_HASH_CYAN 0x70ae2e90c1bce27a
#define MOD_HASH_WHITE 0xced973885856e206
#define MOD_HASH_DARK_GREY 0x55a19de854654d99
#define MOD_HASH_BRIGHT_RED 0xbad8e3fe841b9385
#define MOD_HASH_BRIGHT_GREEN 0x11cc5e579bdd2fb9
#define MOD_HASH_BRIGHT_YELLOW 0xfd579007fe8579f6
#define MOD_HASH_BRIGHT_BLUE 0x57c76bf18badb6d6
#define MOD_HASH_BRIGHT_MAGENTA 0xf6ecc6d3fdfec129
#define MOD_HASH_BRIGHT_CYAN 0x03df73fd4e12ec6d
#define MOD_HASH_BRIGHT_WHITE 0xb5ebc3323f57d7fb
#define MOD_HASH_BG_BLACK 0xd87a8f2d9d394432
#define MOD_HASH_BG_RED 0x145b1e4366c7d7aa
#define MOD_HASH_BG_GREEN 0xa00b8541d3b1e55a
#define MOD_HASH_BG_YELLOW 0x98b030fd86e3b3cf
#define MOD_HASH_BG_BLUE 0xa15529109506b5df
#define MOD_HASH_BG_MAGENTA 0x86dbda99bcc86222
#define MOD_HASH_BG_CYAN 0xf16a3104cf61a098
#define MOD_HASH_BG_WHITE 0x3408c46ab5836674
#define MOD_HASH_BG_DARK_GREY 0x820d2e77568eec47
#define MOD_HASH_BRIGHT_BG_RED 0x144f5dc138087701
#define MOD_HASH_BRIGHT_BG_GREEN 0xc4d88c6426ffe355
#define MOD_HASH_BRIGHT_BG_YELLOW 0xf7bb000a4a792602
#define MOD_HASH_BRIGHT_BG_BLUE 0x9b5c16d6807a1002
#define MOD_HASH_BRIGHT_BG_MAGENTA 0xc59fb2196cdba3fd
#define MOD_HASH_BRIGHT_BG_CYAN 0x46feb6dc999a6f09
#define MOD_HASH_BRIGHT_BG_WHITE 0xa3e7d1da08826f5f
#define MOD_HASH_BOLD 0xcd32ea9bc6b26ff6
#define MOD_HASH_ULINE 0x141fe741e9f8c22a
#define MOD_HASH_ITALIC 0x69d5e5f057d8992d
#define MOD_HASH_INVERT 0xab4ab85ddd6232e1
#define MOD_HASH_RESET 0x0f136ff2c086b760
#define COMPARE_MOD_NAME(ss, sd, hs, hd) ((hs) == (hd) && !strcmp(ss, sd))
static void apply_code_to_vmode(struct tty_format_buf *fmt)
{
const char *modifier = fmt->buf;
uint64_t mod_hash = b_hash_string(modifier);
if (COMPARE_MOD_NAME(modifier, "black", mod_hash, MOD_HASH_BLACK)) {
fmt->vmode.v_fg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = B_TTY_COLOUR16_BLACK;
}
if (COMPARE_MOD_NAME(modifier, "red", mod_hash, MOD_HASH_RED)) {
fmt->vmode.v_fg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = B_TTY_COLOUR16_RED;
}
if (COMPARE_MOD_NAME(modifier, "green", mod_hash, MOD_HASH_GREEN)) {
fmt->vmode.v_fg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = B_TTY_COLOUR16_GREEN;
}
if (COMPARE_MOD_NAME(modifier, "yellow", mod_hash, MOD_HASH_YELLOW)) {
fmt->vmode.v_fg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = B_TTY_COLOUR16_YELLOW;
}
if (COMPARE_MOD_NAME(modifier, "blue", mod_hash, MOD_HASH_BLUE)) {
fmt->vmode.v_fg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = B_TTY_COLOUR16_BLUE;
}
if (COMPARE_MOD_NAME(modifier, "magenta", mod_hash, MOD_HASH_MAGENTA)) {
fmt->vmode.v_fg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = B_TTY_COLOUR16_MAGENTA;
}
if (COMPARE_MOD_NAME(modifier, "cyan", mod_hash, MOD_HASH_CYAN)) {
fmt->vmode.v_fg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = B_TTY_COLOUR16_CYAN;
}
if (COMPARE_MOD_NAME(modifier, "white", mod_hash, MOD_HASH_WHITE)) {
fmt->vmode.v_fg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = B_TTY_COLOUR16_WHITE;
}
if (COMPARE_MOD_NAME(modifier, "dark_grey", mod_hash, MOD_HASH_DARK_GREY)) {
fmt->vmode.v_fg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = B_TTY_COLOUR16_BRIGHT_BLACK;
}
if (COMPARE_MOD_NAME(modifier, "bright_red", mod_hash, MOD_HASH_BRIGHT_RED)) {
fmt->vmode.v_fg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = B_TTY_COLOUR16_BRIGHT_RED;
}
if (COMPARE_MOD_NAME(modifier, "bright_green", mod_hash, MOD_HASH_BRIGHT_GREEN)) {
fmt->vmode.v_fg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = B_TTY_COLOUR16_BRIGHT_GREEN;
}
if (COMPARE_MOD_NAME(modifier, "bright_yellow", mod_hash, MOD_HASH_BRIGHT_YELLOW)) {
fmt->vmode.v_fg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = B_TTY_COLOUR16_BRIGHT_YELLOW;
}
if (COMPARE_MOD_NAME(modifier, "bright_blue", mod_hash, MOD_HASH_BRIGHT_BLUE)) {
fmt->vmode.v_fg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = B_TTY_COLOUR16_BRIGHT_BLUE;
}
if (COMPARE_MOD_NAME(modifier, "bright_magenta", mod_hash, MOD_HASH_BRIGHT_MAGENTA)) {
fmt->vmode.v_fg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = B_TTY_COLOUR16_BRIGHT_MAGENTA;
}
if (COMPARE_MOD_NAME(modifier, "bright_cyan", mod_hash, MOD_HASH_BRIGHT_CYAN)) {
fmt->vmode.v_fg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = B_TTY_COLOUR16_BRIGHT_CYAN;
}
if (COMPARE_MOD_NAME(modifier, "bright_white", mod_hash, MOD_HASH_BRIGHT_WHITE)) {
fmt->vmode.v_fg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = B_TTY_COLOUR16_BRIGHT_WHITE;
}
if (COMPARE_MOD_NAME(modifier, "bg_black", mod_hash, MOD_HASH_BG_BLACK)) {
fmt->vmode.v_bg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = B_TTY_COLOUR16_BLACK;
}
if (COMPARE_MOD_NAME(modifier, "bg_red", mod_hash, MOD_HASH_BG_RED)) {
fmt->vmode.v_bg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = B_TTY_COLOUR16_RED;
}
if (COMPARE_MOD_NAME(modifier, "bg_green", mod_hash, MOD_HASH_BG_GREEN)) {
fmt->vmode.v_bg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = B_TTY_COLOUR16_GREEN;
}
if (COMPARE_MOD_NAME(modifier, "bg_yellow", mod_hash, MOD_HASH_BG_YELLOW)) {
fmt->vmode.v_bg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = B_TTY_COLOUR16_YELLOW;
}
if (COMPARE_MOD_NAME(modifier, "bg_blue", mod_hash, MOD_HASH_BG_BLUE)) {
fmt->vmode.v_bg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = B_TTY_COLOUR16_BLUE;
}
if (COMPARE_MOD_NAME(modifier, "bg_magenta", mod_hash, MOD_HASH_BG_MAGENTA)) {
fmt->vmode.v_bg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = B_TTY_COLOUR16_MAGENTA;
}
if (COMPARE_MOD_NAME(modifier, "bg_cyan", mod_hash, MOD_HASH_BG_CYAN)) {
fmt->vmode.v_bg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = B_TTY_COLOUR16_CYAN;
}
if (COMPARE_MOD_NAME(modifier, "bg_white", mod_hash, MOD_HASH_BG_WHITE)) {
fmt->vmode.v_bg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = B_TTY_COLOUR16_WHITE;
}
if (COMPARE_MOD_NAME(modifier, "bg_dark_grey", mod_hash, MOD_HASH_BG_DARK_GREY)) {
fmt->vmode.v_bg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = B_TTY_COLOUR16_BRIGHT_BLACK;
}
if (COMPARE_MOD_NAME(modifier, "bright_bg_red", mod_hash, MOD_HASH_BRIGHT_BG_RED)) {
fmt->vmode.v_bg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = B_TTY_COLOUR16_BRIGHT_RED;
}
if (COMPARE_MOD_NAME(modifier, "bright_bg_green", mod_hash, MOD_HASH_BRIGHT_BG_GREEN)) {
fmt->vmode.v_bg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = B_TTY_COLOUR16_BRIGHT_GREEN;
}
if (COMPARE_MOD_NAME(modifier, "bright_bg_yellow", mod_hash, MOD_HASH_BRIGHT_BG_YELLOW)) {
fmt->vmode.v_bg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = B_TTY_COLOUR16_BRIGHT_YELLOW;
}
if (COMPARE_MOD_NAME(modifier, "bright_bg_blue", mod_hash, MOD_HASH_BRIGHT_BG_BLUE)) {
fmt->vmode.v_bg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = B_TTY_COLOUR16_BRIGHT_BLUE;
}
if (COMPARE_MOD_NAME(modifier, "bright_bg_magenta", mod_hash, MOD_HASH_BRIGHT_BG_MAGENTA)) {
fmt->vmode.v_bg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = B_TTY_COLOUR16_BRIGHT_MAGENTA;
}
if (COMPARE_MOD_NAME(modifier, "bright_bg_cyan", mod_hash, MOD_HASH_BRIGHT_BG_CYAN)) {
fmt->vmode.v_bg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = B_TTY_COLOUR16_BRIGHT_CYAN;
}
if (COMPARE_MOD_NAME(modifier, "bright_bg_white", mod_hash, MOD_HASH_BRIGHT_BG_WHITE)) {
fmt->vmode.v_bg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = B_TTY_COLOUR16_BRIGHT_WHITE;
}
if (COMPARE_MOD_NAME(modifier, "bold", mod_hash, MOD_HASH_BOLD)) {
fmt->vmode.v_attrib |= B_TTY_ATTRIB_BOLD;
}
if (COMPARE_MOD_NAME(modifier, "uline", mod_hash, MOD_HASH_ULINE)) {
fmt->vmode.v_attrib |= B_TTY_ATTRIB_UNDERLINE;
}
if (COMPARE_MOD_NAME(modifier, "italic", mod_hash, MOD_HASH_ITALIC)) {
fmt->vmode.v_attrib |= B_TTY_ATTRIB_ITALIC;
}
if (COMPARE_MOD_NAME(modifier, "invert", mod_hash, MOD_HASH_INVERT)) {
fmt->vmode.v_attrib |= B_TTY_ATTRIB_INVERT;
}
if (COMPARE_MOD_NAME(modifier, "reset", mod_hash, MOD_HASH_RESET)) {
fmt->vmode.v_fg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_bg.c_mode = B_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = B_TTY_COLOUR16_WHITE;
fmt->vmode.v_bg.c_16.value = B_TTY_COLOUR16_BLACK;
fmt->vmode.v_attrib = 0;
}
}
static void format_buffer_putc(struct tty_format_buf* fmt, int c)
{
if (fmt->ptr < TTY_TMPBUF_SIZE - 1) {
fmt->buf[fmt->ptr++] = c;
fmt->buf[fmt->ptr] = '\0';
}
}
static void format_buffer_clear(struct tty_format_buf* fmt)
{
fmt->ptr = 0;
fmt->buf[fmt->ptr] = '\0';
}
static void flush_vmode(struct b_tty* tty, struct tty_format_buf* fmt)
{
b_tty_set_vmode(tty, &fmt->vmode);
}
int b_tty_putc(struct b_tty *tty, enum b_tty_print_flags flags, char c)
{
if (flags & B_TTY_DISABLE_FORMATTING) {
z__b_tty_putc(tty, c);
return c;
}
struct tty_format_buf *fmt = z__b_tty_get_format_buf(tty);
if (c == '[') {
if (!fmt->in_format) {
fmt->in_format = true;
return c;
}
if (fmt->ptr == 0) {
fmt->in_format = false;
z__b_tty_putc(tty, '[');
return c;
}
format_buffer_putc(fmt, c);
return c;
}
if (fmt->in_format && c == ']') {
apply_code_to_vmode(fmt);
format_buffer_clear(fmt);
flush_vmode(tty, fmt);
fmt->in_format = false;
return c;
}
if (fmt->in_format && c == ',') {
apply_code_to_vmode(fmt);
format_buffer_clear(fmt);
return c;
}
if (fmt->in_format) {
format_buffer_putc(fmt, c);
} else {
z__b_tty_putc(tty, c);
}
return c;
}
int b_tty_puts(
struct b_tty* tty, enum b_tty_print_flags flags, const char* s)
{
int r = 0;
while (s[r]) {
b_tty_putc(tty, flags, s[r]);
r++;
}
return r;
}
int b_tty_printf(
struct b_tty* tty, enum b_tty_print_flags flags, const char* s, ...)
{
va_list arg;
va_start(arg, s);
int r = b_tty_vprintf(tty, flags, s, arg);
va_end(arg);
return r;
}

19
term/tty.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef _TTY_H_
#define _TTY_H_
#define TTY_TMPBUF_SIZE 128
struct b_tty;
struct tty_format_buf {
bool in_format;
char buf[TTY_TMPBUF_SIZE];
unsigned int ptr;
struct b_tty_vmode vmode;
};
extern struct tty_format_buf *z__b_tty_get_format_buf(struct b_tty *tty);
extern void z__b_tty_putc(struct b_tty *tty, char c);
#endif