term: extend tty interface with more features
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
45
term/include/blue/term/print.h
Normal file
45
term/include/blue/term/print.h
Normal 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
|
||||||
179
term/include/blue/term/tty.h
Normal file
179
term/include/blue/term/tty.h
Normal 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
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
279
term/print.c
279
term/print.c
@@ -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_puts(const char* s)
|
||||||
}
|
|
||||||
|
|
||||||
int b_fputs(const char *str, FILE *fp)
|
|
||||||
{
|
{
|
||||||
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
996
term/printf.c
Normal 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
51
term/printf.h
Normal 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
461
term/sys/darwin/tty.c
Normal 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
461
term/sys/linux/tty.c
Normal 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
570
term/sys/windows/tty.c
Normal 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
330
term/tty.c
Normal 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
19
term/tty.h
Normal 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
|
||||||
Reference in New Issue
Block a user