meta: rename term module to fx.term namespace

This commit is contained in:
2026-05-02 14:37:43 +01:00
parent 274a48a845
commit b8cf2b379b
18 changed files with 0 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
include(../cmake/Templates.cmake)
add_fx_module(NAME term DEPENDENCIES core ds)
+372
View File
@@ -0,0 +1,372 @@
#include <fx/core/error.h>
#include <fx/core/stringstream.h>
#include <fx/term/tty.h>
#include <inttypes.h>
static void get_error_id(
const struct fx_error_vendor *vendor, const struct fx_error *error,
char *out, size_t max)
{
const char *vendor_name = NULL;
const char *error_name = NULL;
fx_error_status_code code = fx_error_get_status_code(error);
if (vendor) {
vendor_name = vendor->v_name;
error_name = fx_error_vendor_get_status_code_name(vendor, code);
}
fx_stringstream *id = fx_stringstream_create_with_buffer(out, max);
if (vendor_name) {
fx_stream_write_cstr(id, vendor_name, NULL);
}
if (error_name) {
if (vendor_name) {
fx_stream_write_cstr(id, ".", NULL);
}
fx_stream_write_cstr(id, error_name, NULL);
} else {
if (vendor_name) {
fx_stream_write_cstr(id, "#", NULL);
}
fx_stream_write_fmt(id, NULL, "%ld", code);
}
}
static void print_template_parameter(
const struct fx_error_template_parameter *params, size_t nr_params,
const char *param_name)
{
const struct fx_error_template_parameter *param = NULL;
for (size_t i = 0; i < nr_params; i++) {
if (!params[i].param_name) {
break;
}
if (!strcmp(params[i].param_name, param_name)) {
param = &params[i];
break;
}
}
if (!param) {
return;
}
const char *format = param->__param_def->param_format;
switch (param->__param_def->param_type) {
case FX_ERROR_TEMPLATE_PARAM_STRING: {
const char *s = (const char *)param->param_value;
fx_tty_printf(fx_stdtty_err, 0, format, s);
break;
}
case FX_ERROR_TEMPLATE_PARAM_CHAR: {
char v = (char)param->param_value;
fx_tty_printf(fx_stdtty_err, 0, format, v);
break;
}
case FX_ERROR_TEMPLATE_PARAM_INT: {
int v = (int)param->param_value;
fx_tty_printf(fx_stdtty_err, 0, format, v);
break;
}
case FX_ERROR_TEMPLATE_PARAM_UINT: {
unsigned int v = (unsigned int)param->param_value;
fx_tty_printf(fx_stdtty_err, 0, format, v);
break;
}
case FX_ERROR_TEMPLATE_PARAM_LONG: {
long v = (long)param->param_value;
fx_tty_printf(fx_stdtty_err, 0, format, v);
break;
}
case FX_ERROR_TEMPLATE_PARAM_ULONG: {
unsigned long v = (unsigned long)param->param_value;
fx_tty_printf(fx_stdtty_err, 0, format, v);
break;
}
case FX_ERROR_TEMPLATE_PARAM_LONGLONG: {
long long v = (long long)param->param_value;
fx_tty_printf(fx_stdtty_err, 0, format, v);
break;
}
case FX_ERROR_TEMPLATE_PARAM_ULONGLONG: {
unsigned long long v = (unsigned long long)param->param_value;
fx_tty_printf(fx_stdtty_err, 0, format, v);
break;
}
case FX_ERROR_TEMPLATE_PARAM_PTR: {
const void *p = (const void *)param->param_value;
fx_tty_printf(fx_stdtty_err, 0, format, p);
break;
}
case FX_ERROR_TEMPLATE_PARAM_INTPTR: {
intptr_t v = (intptr_t)param->param_value;
fx_tty_printf(fx_stdtty_err, 0, format, v);
break;
}
case FX_ERROR_TEMPLATE_PARAM_UINTPTR: {
uintptr_t v = (uintptr_t)param->param_value;
fx_tty_printf(fx_stdtty_err, 0, format, v);
break;
}
default:
return;
}
}
static void print_content(
const struct fx_error_template_parameter *params, size_t nr_params,
const char *s)
{
if (!s) {
return;
}
char modifier = 0;
char buf[128];
size_t buf_len = 0;
size_t i;
for (i = 0; s[i];) {
char c = s[i];
if (c != '@') {
fx_tty_putc(fx_stdtty_err, 0, c);
i++;
continue;
}
i++;
modifier = s[i];
if (!modifier) {
break;
}
if (modifier != '[' && modifier != '{') {
i++;
}
if (s[i] == '@') {
fx_tty_putc(fx_stdtty_err, 0, c);
break;
}
bool template_param;
char end = 0;
switch (s[i]) {
case '{':
template_param = false;
end = '}';
break;
case '[':
template_param = true;
end = ']';
break;
default:
return;
}
switch (modifier) {
case 'i':
fx_tty_printf(fx_stdtty_err, 0, "[cyan]");
break;
case 'w':
fx_tty_printf(fx_stdtty_err, 0, "[yellow]");
break;
case 'e':
fx_tty_printf(fx_stdtty_err, 0, "[red]");
break;
default:
break;
}
buf_len = 0;
i++;
while (s[i] && s[i] != end) {
buf[buf_len++] = s[i];
buf[buf_len] = 0;
i++;
}
if (template_param) {
print_template_parameter(params, nr_params, buf);
} else {
fx_tty_printf(fx_stdtty_err, 0, "%s", buf);
}
fx_tty_printf(fx_stdtty_err, 0, "[reset]");
if (s[i] != 0) {
i++;
}
}
}
static void print_submsg(const struct fx_error *error)
{
const struct fx_error_definition *error_def = fx_error_get_definition(error);
const struct fx_error_submsg *submsg = fx_error_get_first_submsg(error);
while (submsg) {
enum fx_error_submsg_type type = fx_error_submsg_get_type(submsg);
const char *content = fx_error_submsg_get_content(submsg);
const struct fx_error_msg *msg = fx_error_submsg_get_msg(submsg);
const struct fx_error_template_parameter *params
= fx_error_submsg_get_template_parameters(submsg);
fx_tty_printf(fx_stdtty_err, 0, " ");
switch (type) {
case FX_ERROR_SUBMSG_ERROR:
fx_tty_printf(
fx_stdtty_err, 0, "[bright_red,bold]>[reset] ");
break;
case FX_ERROR_SUBMSG_WARNING:
fx_tty_printf(
fx_stdtty_err, 0,
"[bright_yellow,bold]>[reset] ");
break;
case FX_ERROR_SUBMSG_INFO:
fx_tty_printf(
fx_stdtty_err, 0, "[bright_cyan,bold]>[reset] ");
break;
default:
break;
}
if (msg) {
print_content(
params, FX_ERROR_TEMPLATE_PARAMETER_MAX,
fx_error_msg_get_content(msg));
} else if (content) {
print_content(
params, FX_ERROR_TEMPLATE_PARAMETER_MAX, content);
}
fx_tty_printf(fx_stdtty_err, 0, "\n");
submsg = fx_error_get_next_submsg(error, submsg);
}
}
const char *get_short_filepath(const char *path)
{
size_t len = strlen(path);
unsigned int sep_count = 0;
for (size_t i = len - 1; i > 0; i--) {
if (path[i] != '/' && path[i] != '\\') {
continue;
}
sep_count++;
if (sep_count == 2) {
return path + i + 1;
}
}
return path;
}
static void print_stack_trace(const struct fx_error *error)
{
const struct fx_error_stack_frame *frame
= fx_error_get_first_stack_frame(error);
while (frame) {
const char *file, *func;
unsigned int line;
file = fx_error_stack_frame_get_filepath(frame);
line = fx_error_stack_frame_get_line_number(frame);
func = fx_error_stack_frame_get_function_name(frame);
file = get_short_filepath(file);
fx_tty_printf(
fx_stdtty_err, 0,
" [dark_grey]at %s() (%s:%u)[reset]\n", func, file,
line);
frame = fx_error_get_next_stack_frame(error, frame);
}
}
static void report_error(
const fx_error *error, fx_error_report_flags flags, bool caused_by)
{
const fx_error_vendor *vendor = fx_error_get_vendor(error);
char error_id[128];
get_error_id(vendor, error, error_id, sizeof error_id);
const struct fx_error_definition *error_def = fx_error_get_definition(error);
fx_error_status_code code = fx_error_get_status_code(error);
const char *description = fx_error_get_description(error);
const struct fx_error_template_parameter *params
= fx_error_get_template_parameters(error);
if (!description && vendor) {
description = fx_error_vendor_get_status_code_description(
vendor, code);
}
if (caused_by) {
fx_tty_printf(
fx_stdtty_err, 0,
" [green]->[reset] caused by [bright_red,bold]ERROR ");
} else {
fx_tty_printf(fx_stdtty_err, 0, "[bright_red,bold]==> ERROR ");
}
if (flags & FX_ERROR_REPORT_STATUS) {
fx_tty_printf(fx_stdtty_err, 0, "%s", error_id);
}
fx_tty_printf(fx_stdtty_err, 0, "[reset]");
if (flags & FX_ERROR_REPORT_DESCRIPTION) {
const struct fx_error_msg *msg = fx_error_get_msg(error);
if (msg) {
fx_tty_printf(fx_stdtty_err, 0, ": ");
print_content(
params, FX_ERROR_TEMPLATE_PARAMETER_MAX,
fx_error_msg_get_content(msg));
} else if (description) {
fx_tty_printf(fx_stdtty_err, 0, ": ");
print_content(
params, FX_ERROR_TEMPLATE_PARAMETER_MAX,
description);
}
}
fx_tty_printf(fx_stdtty_err, 0, "\n");
if (flags & FX_ERROR_REPORT_SUBMSG) {
print_submsg(error);
}
if (flags & FX_ERROR_REPORT_STACK_TRACE) {
print_stack_trace(error);
}
const struct fx_error *cause = fx_error_get_caused_by(error);
if (cause && (flags & FX_ERROR_REPORT_CAUSE)) {
report_error(cause, flags, true);
}
}
void fx_enhanced_error_reporter(
const struct fx_error *error, fx_error_report_flags flags)
{
report_error(error, flags, false);
}
+10
View File
@@ -0,0 +1,10 @@
#ifndef FX_TERM_H_
#define FX_TERM_H_
#include <fx/core/status.h>
#include <stdio.h>
#include <fx/term/tty.h>
#include <fx/term/print.h>
#endif
+52
View File
@@ -0,0 +1,52 @@
#ifndef FX_TERM_PRINT_H_
#define FX_TERM_PRINT_H_
#include <fx/core/misc.h>
#include <fx/core/status.h>
#include <fx/term/tty.h>
#include <stdarg.h>
#define fx_i(...) fx_print(FX_PRINT_I, __VA_ARGS__)
#define fx_warn(...) fx_print(FX_PRINT_WARN, __VA_ARGS__)
#define fx_err(...) fx_print(FX_PRINT_ERR, __VA_ARGS__)
struct fx_tty;
struct fx_error;
struct fx_error_vendor;
enum fx_error_report_flags;
typedef enum fx_paragraph_format_flags {
FX_PARAGRAPH_DONT_INDENT_FIRST_LINE = 0x01u,
FX_PARAGRAPH_HYPHENATE = 0x02u,
FX_PARAGRAPH_DOUBLE_LINE_BREAK = 0x04u,
FX_PARAGRAPH_MULTI_LINE_BREAK = 0x08u,
} fx_paragraph_format_flags;
typedef struct fx_paragraph_format {
fx_paragraph_format_flags p_flags;
unsigned int p_left_margin;
unsigned int p_right_margin;
unsigned int p_line_length;
} fx_paragraph_format;
typedef enum fx_print_format {
FX_PRINT_NORMAL = 0,
FX_PRINT_I,
FX_PRINT_WARN,
FX_PRINT_ERR,
} fx_print_format;
FX_API fx_status fx_print(fx_print_format format, const char *str, ...);
FX_API fx_status fx_print_paragraph(
const char *str, struct fx_tty *tty, fx_paragraph_format *format);
FX_API int fx_putc(char c);
FX_API int fx_puts(const char *s);
FX_API int fx_printf(const char *format, ...);
FX_API void fx_enhanced_error_reporter(
const struct fx_error *, enum fx_error_report_flags flags);
#endif
+180
View File
@@ -0,0 +1,180 @@
#ifndef FX_TERM_TTY_H_
#define FX_TERM_TTY_H_
#include <fx/core/misc.h>
#include <fx/core/status.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#define fx_stdtty (z__fx_tty_get_std())
#define fx_stdtty_err (z__fx_tty_get_err())
#define FX_TTY_CTRL_KEY(c) ((c) | FX_MOD_CTRL)
#define FX_MAKE_VMODE(fg, bg, a) \
{ \
.v_fg = fg, .v_bg = bg, .v_attrib = (a) \
}
#define FX_MAKE_COLOUR_DEFAULT(v) \
{ \
.c_mode = TTY_COLOUR_NONE, \
}
#define FX_MAKE_COLOUR_16(v) \
{ \
.c_mode = TTY_COLOUR_16, .c_16 = {.value = (v) } \
}
#define FX_MAKE_COLOUR_256(v) \
{ \
.c_mode = TTY_COLOUR_256, .c_256 = {.value = (v) } \
}
#define FX_MAKE_COLOUR_TRUE(cr, cg, cb) \
{ \
.c_mode = TTY_COLOUR_TRUE, .c_true \
= {.r = (cr), \
.g = (cg), \
.b = (cb) } \
}
typedef struct fx_tty fx_tty;
typedef uint32_t fx_keycode;
/* codepoints U+F0000 to U+FFFFD are reserved areas in Unicode, free for
* application use. store special keycodes here */
enum {
FX_KEY_ARROW_LEFT = 0xF0000,
FX_KEY_ARROW_RIGHT,
FX_KEY_ARROW_UP,
FX_KEY_ARROW_DOWN,
FX_KEY_BACKSPACE,
FX_KEY_RETURN,
FX_MOD_CTRL = 0x10000000,
FX_KEY_EOF = 0xFFFFFFFF,
};
typedef enum fx_tty_colour16 {
FX_TTY_COLOUR16_BLACK = 0,
FX_TTY_COLOUR16_RED,
FX_TTY_COLOUR16_GREEN,
FX_TTY_COLOUR16_YELLOW,
FX_TTY_COLOUR16_FX,
FX_TTY_COLOUR16_MAGENTA,
FX_TTY_COLOUR16_CYAN,
FX_TTY_COLOUR16_WHITE,
FX_TTY_COLOUR16_BRIGHT_BLACK,
FX_TTY_COLOUR16_BRIGHT_RED,
FX_TTY_COLOUR16_BRIGHT_GREEN,
FX_TTY_COLOUR16_BRIGHT_YELLOW,
FX_TTY_COLOUR16_BRIGHT_FX,
FX_TTY_COLOUR16_BRIGHT_MAGENTA,
FX_TTY_COLOUR16_BRIGHT_CYAN,
FX_TTY_COLOUR16_BRIGHT_WHITE,
} fx_tty_colour16;
typedef enum fx_tty_colour_mode {
FX_TTY_COLOUR_NONE = 0,
FX_TTY_COLOUR_16,
FX_TTY_COLOUR_256,
FX_TTY_COLOUR_TRUE,
} fx_tty_colour_mode;
typedef enum fx_tty_position_base {
FX_TTY_POS_START,
FX_TTY_POS_CURSOR,
} fx_tty_position_base;
typedef enum fx_tty_attrib {
FX_TTY_ATTRIB_NORMAL = 0x00u,
FX_TTY_ATTRIB_BOLD = 0x01u,
FX_TTY_ATTRIB_UNDERLINE = 0x02u,
FX_TTY_ATTRIB_ITALIC = 0x04u,
FX_TTY_ATTRIB_INVERT = 0x08u,
} fx_tty_attrib;
typedef enum fx_tty_clear_mode {
FX_TTY_CLEAR_LINE = 0x01u,
FX_TTY_CLEAR_SCREEN = 0x02,
FX_TTY_CLEAR_TO_CURSOR = 0x0100u,
FX_TTY_CLEAR_FROM_CURSOR = 0x0200u,
FX_TTY_CLEAR_ALL = 0x0400u,
} fx_tty_clear_mode;
typedef enum fx_tty_mode {
FX_TTY_CANONICAL = 0x00u,
FX_TTY_RAW = 0x01u,
} fx_tty_mode;
typedef enum fx_tty_print_flags {
FX_TTY_DISABLE_FORMATTING = 0x01u,
FX_TTY_DISABLE_INTERPOLATED_FORMATTING = 0x02u,
} fx_tty_print_flags;
typedef struct fx_tty_colour {
enum fx_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;
};
} fx_tty_colour;
typedef struct fx_tty_vmode {
enum fx_tty_attrib v_attrib;
struct fx_tty_colour v_fg;
struct fx_tty_colour v_bg;
} fx_tty_vmode;
FX_API fx_tty *z__fx_tty_get_std(void);
FX_API fx_tty *z__fx_tty_get_err(void);
static inline unsigned int fx_keycode_get_key(fx_keycode v)
{
return v & 0x0FFFFFFF;
}
FX_API bool fx_tty_is_interactive(const struct fx_tty *tty);
FX_API void fx_tty_set_mode(struct fx_tty *tty, enum fx_tty_mode mode);
FX_API void fx_tty_set_vmode(struct fx_tty *tty, const struct fx_tty_vmode *vmode);
FX_API void fx_tty_reset_vmode(struct fx_tty *tty);
FX_API enum fx_status fx_tty_get_dimensions(
struct fx_tty *tty, unsigned int *w, unsigned int *h);
FX_API enum fx_status fx_tty_get_cursor_position(
struct fx_tty *tty, unsigned int *x, unsigned int *y);
FX_API fx_keycode fx_tty_read_key(struct fx_tty *tty);
FX_API void fx_tty_move_cursor_x(
struct fx_tty *tty, enum fx_tty_position_base base, int pos);
FX_API void fx_tty_move_cursor_y(
struct fx_tty *tty, enum fx_tty_position_base base, int pos);
FX_API void fx_tty_clear(struct fx_tty *tty, enum fx_tty_clear_mode mode);
FX_API int fx_tty_putc(struct fx_tty *tty, enum fx_tty_print_flags flags, char c);
FX_API int fx_tty_puts(
struct fx_tty *tty, enum fx_tty_print_flags flags, const char *s);
FX_API int fx_tty_printf(
struct fx_tty *tty, enum fx_tty_print_flags flags, const char *s, ...);
FX_API int fx_tty_vprintf(
struct fx_tty *tty, enum fx_tty_print_flags flags, const char *s,
va_list args);
#endif
+176
View File
@@ -0,0 +1,176 @@
#include <fx/ds/string.h>
#include <fx/term/print.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DEFAULT_PARAGRAPH_WIDTH 160
static void indent(
struct fx_paragraph_format *format, struct fx_tty *tty, unsigned int margin)
{
unsigned int x = 0;
while (x < margin) {
fx_tty_puts(tty, 0, " ");
x++;
}
}
static unsigned int extract_line(
const char **sp, unsigned int line_length, fx_string *out,
struct fx_paragraph_format *format)
{
const char *start = *sp;
while (isspace(*start) || *start == '\n') {
start++;
}
if (!*start) {
return 0;
}
const char *end = NULL;
unsigned int delta = 0;
unsigned int i;
for (i = 0; start[i]; i++) {
if (start[i] == '\033') {
while (start[i] && !isalpha(start[i])) {
delta++;
i++;
}
delta++;
}
if (start[i] == '[') {
if (start[i + 1] == '[') {
delta++;
i++;
continue;
}
while (start[i] && start[i] != ']') {
i++;
delta++;
}
delta++;
}
if (!isspace(start[i]) && start[i] != '\n') {
continue;
}
if ((i - delta) >= line_length) {
break;
}
end = &start[i];
if (start[i] == '\n') {
break;
}
}
if (start[i] == '\0' && (i - delta) < line_length) {
end = NULL;
}
unsigned int len;
if (end) {
len = end - start;
} else {
len = strlen(start);
}
fx_string_insert_cstrn(out, start, len, FX_NPOS);
*sp = end;
return len;
}
static fx_status print_paragraph_tty(
const char *str, struct fx_tty *tty, struct fx_paragraph_format *format)
{
unsigned int w = 0, h = 0;
fx_tty_get_dimensions(tty, &w, &h);
if (!w) {
w = DEFAULT_PARAGRAPH_WIDTH;
}
unsigned int left_margin = format->p_left_margin;
unsigned int line_length = format->p_line_length;
if (format->p_left_margin + format->p_right_margin >= w) {
return FX_SUCCESS;
}
unsigned int page_width
= w - format->p_left_margin - format->p_right_margin;
if (page_width < line_length || line_length == 0) {
line_length = page_width;
}
if (!(format->p_flags & FX_PARAGRAPH_DONT_INDENT_FIRST_LINE)) {
indent(format, tty, left_margin);
}
fx_string *line = fx_string_create();
bool need_indent = false;
while (str) {
if (*str == '\n') {
if (format->p_flags & FX_PARAGRAPH_DOUBLE_LINE_BREAK) {
fx_tty_putc(tty, 0, '\n');
}
str++;
need_indent = true;
while (*str == '\n') {
if (format->p_flags & FX_PARAGRAPH_MULTI_LINE_BREAK) {
fx_tty_putc(tty, 0, '\n');
}
str++;
}
}
fx_string_clear(line);
unsigned int this_line
= extract_line(&str, line_length, line, format);
if (this_line == 0) {
break;
}
if (need_indent) {
indent(format, tty, left_margin);
}
fx_tty_puts(tty, 0, fx_string_get_cstr(line));
fx_tty_putc(tty, 0, '\n');
need_indent = true;
}
fx_string_unref(line);
return FX_SUCCESS;
}
static fx_status print_paragraph_file(
const char *str, FILE *fp, struct fx_paragraph_format *format)
{
return FX_SUCCESS;
}
fx_status fx_print_paragraph(
const char *str, struct fx_tty *fp, struct fx_paragraph_format *format)
{
return print_paragraph_tty(str, fp, format);
}
+147
View File
@@ -0,0 +1,147 @@
#include "print.h"
#include <fx/core/hash.h>
#include <fx/term/print.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#define F_BLACK "[black]"
#define F_RED "[red]"
#define F_YEL "[yellow]"
#define F_CYN "[cyan]"
#define F_BLACK_BOLD "[bold,black]"
#define F_RED_BOLD "[bold,red]"
#define F_YEL_BOLD "[bold,yellow]"
#define F_CYN_BOLD "[bold,cyan]"
#define F_BLACK_BG "[bg_black]"
#define F_RED_BG "[bg_reg]"
#define F_YEL_BG "[bg_yellow]"
#define F_CYN_BG "[bg_cyan]"
#define F_RESET "[reset]"
typedef fx_status (*print_function)(struct fx_tty *tty, const char *s);
static int print_no_ansi(FILE *fp, const char *str)
{
int out = 0;
for (size_t i = 0; str[i]; i++) {
if (str[i] != '\033') {
fputc(str[i], fp);
out++;
continue;
}
while (str[i] && !isalpha(str[i])) {
i++;
}
}
return out;
}
static fx_status print_normal(struct fx_tty *tty, const char *s)
{
return FX_SUCCESS;
}
static fx_status print_i(struct fx_tty *tty, const char *s)
{
struct fx_paragraph_format format = {0};
format.p_left_margin = 5;
format.p_right_margin = 5;
format.p_flags = FX_PARAGRAPH_DONT_INDENT_FIRST_LINE
| FX_PARAGRAPH_MULTI_LINE_BREAK;
// printf(F_CYN_BG F_BLACK " i " F_RESET " ");
fx_tty_puts(tty, 0, F_CYN_BOLD " i:" F_RESET " ");
fx_print_paragraph(s, tty, &format);
return FX_SUCCESS;
}
static fx_status print_warn(struct fx_tty *tty, const char *s)
{
struct fx_paragraph_format format = {0};
format.p_left_margin = 5;
format.p_right_margin = 5;
format.p_flags = FX_PARAGRAPH_DONT_INDENT_FIRST_LINE
| FX_PARAGRAPH_MULTI_LINE_BREAK;
// printf(F_YEL_BG F_BLACK " Warn " F_RESET " ");
fx_tty_puts(tty, 0, F_YEL_BOLD " !!" F_RESET " ");
fx_print_paragraph(s, tty, &format);
return FX_SUCCESS;
}
static fx_status print_err(struct fx_tty *tty, const char *s)
{
struct fx_paragraph_format format = {0};
format.p_left_margin = 5;
format.p_right_margin = 5;
format.p_flags = FX_PARAGRAPH_DONT_INDENT_FIRST_LINE
| FX_PARAGRAPH_MULTI_LINE_BREAK;
// printf(F_RED_BG F_BLACK " Err " F_RESET " ");
fx_tty_puts(tty, 0, F_RED_BOLD "Err:" F_RESET " ");
fx_print_paragraph(s, tty, &format);
return FX_SUCCESS;
}
static print_function format_printers[] = {
[FX_PRINT_NORMAL] = print_normal,
[FX_PRINT_I] = print_i,
[FX_PRINT_WARN] = print_warn,
[FX_PRINT_ERR] = print_err,
};
static size_t nr_format_printers
= sizeof format_printers / sizeof format_printers[FX_PRINT_NORMAL];
fx_status fx_print(fx_print_format format, const char *str, ...)
{
char buf[1024];
va_list arg;
va_start(arg, str);
vsnprintf(buf, sizeof buf, str, arg);
va_end(arg);
if (format >= nr_format_printers) {
return FX_ERR_INVALID_ARGUMENT;
}
print_function printer = format_printers[format];
if (!printer) {
return FX_ERR_INVALID_ARGUMENT;
}
return printer(fx_stdtty, buf);
}
int fx_putc(char c)
{
return fx_tty_putc(fx_stdtty, 0, c);
}
int fx_puts(const char* s)
{
return fx_tty_puts(fx_stdtty, 0, s);
}
int fx_printf(const char* format, ...)
{
va_list arg;
va_start(arg, format);
int x = fx_tty_vprintf(
fx_stdtty, FX_TTY_DISABLE_INTERPOLATED_FORMATTING, format, arg);
va_end(arg);
return x;
}
+53
View File
@@ -0,0 +1,53 @@
#ifndef _FX_PRINT_H_
#define _FX_PRINT_H_
#include <fx/core/misc.h>
#include <fx/term.h>
#include <stdio.h>
enum z__fx_stream_modifier {
Z__FX_STREAM_MOD_BLACK = 0x1,
Z__FX_STREAM_MOD_RED = 0x2,
Z__FX_STREAM_MOD_GREEN = 0x4,
Z__FX_STREAM_MOD_FX = 0x8,
Z__FX_STREAM_MOD_BRIGHT = 0x10,
Z__FX_STREAM_MOD_BG_BLACK = 0x20,
Z__FX_STREAM_MOD_BG_RED = 0x40,
Z__FX_STREAM_MOD_BG_GREEN = 0x80,
Z__FX_STREAM_MOD_BG_FX = 0x100,
Z__FX_STREAM_MOD_BG_BRIGHT = 0x200,
Z__FX_STREAM_MOD_BOLD = 0x400,
Z__FX_STREAM_MOD_ITALIC = 0x800,
Z__FX_STREAM_MOD_ULINE = 0x1000,
Z__FX_STREAM_MOD_INVERT = 0x2000,
Z__FX_STREAM_MOD_RESET = 0x4000,
};
#define Z__FX_STREAM_MOD_GET_FG_COLOUR(x) \
((x) \
& (Z__FX_STREAM_MOD_BLACK | Z__FX_STREAM_MOD_RED \
| Z__FX_STREAM_MOD_GREEN | Z__FX_STREAM_MOD_FX))
#define Z__FX_STREAM_MOD_CLEAR_FG_COLOUR(x) \
((x) \
& ~(Z__FX_STREAM_MOD_BLACK | Z__FX_STREAM_MOD_RED \
| Z__FX_STREAM_MOD_GREEN | Z__FX_STREAM_MOD_FX))
#define Z__FX_STREAM_MOD_GET_BG_COLOUR(x) \
((x) \
& (Z__FX_STREAM_MOD_BG_BLACK | Z__FX_STREAM_MOD_BG_RED \
| Z__FX_STREAM_MOD_BG_GREEN | Z__FX_STREAM_MOD_BG_FX))
#define Z__FX_STREAM_MOD_CLEAR_BG_COLOUR(x) \
((x) \
& ~(Z__FX_STREAM_MOD_BG_BLACK | Z__FX_STREAM_MOD_BG_RED \
| Z__FX_STREAM_MOD_BG_GREEN | Z__FX_STREAM_MOD_BG_FX))
FX_API int z__fx_stream_is_tty(FILE *fp);
FX_API int z__fx_stream_dimensions(FILE *fp, unsigned int *w, unsigned int *h);
FX_API int z__fx_stream_cursorpos(
FILE *in, FILE *out, unsigned int *x, unsigned int *y);
FX_API int z__fx_stream_set_modifier(FILE *fp, unsigned int mod);
#endif
+982
View File
@@ -0,0 +1,982 @@
///////////////////////////////////////////////////////////////////////////////
// \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 <fx/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 fx_tty *tty;
enum fx_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 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 fx_tty_print_flags flags = args->flags;
if (!args->format_ch && (flags & FX_TTY_DISABLE_INTERPOLATED_FORMATTING)) {
flags |= FX_TTY_DISABLE_FORMATTING;
}
fx_tty_putc(args->tty, flags, c);
}
int fx_tty_vprintf(
struct fx_tty *tty, enum fx_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
View File
@@ -0,0 +1,51 @@
///////////////////////////////////////////////////////////////////////////////
// \author (c) Marco Paland (info@paland.com)
// 2014-2019, PALANDesign Hannover, Germany
//
// \license The MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on
// embedded systems with a very limited resources.
// Use this instead of bloated standard/newlib printf.
// These routines are thread safe and reentrant.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef _PRINTF_H_
#define _PRINTF_H_
#include <stdarg.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif // _PRINTF_H_
+224
View File
@@ -0,0 +1,224 @@
#include "../../print.h"
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
int z__fx_stream_is_tty(FILE *fp)
{
return isatty(fileno(fp));
}
int z__fx_stream_dimensions(FILE *fp, unsigned int *w, unsigned int *h)
{
if (!isatty(fileno(fp))) {
return -1;
}
struct winsize ws;
if (ioctl(fileno(fp), TIOCGWINSZ, &ws) == -1) {
return -1;
}
if (w) {
*w = ws.ws_col;
}
if (h) {
*h = ws.ws_row;
}
return 0;
}
int z__fx_stream_cursorpos(FILE *in, FILE *out, unsigned int *x, unsigned int *y)
{
if (!isatty(fileno(in)) || !isatty(fileno(out))) {
return -1;
}
struct termios term, restore;
tcgetattr(fileno(in), &term);
tcgetattr(fileno(in), &restore);
term.c_lflag &= ~(ICANON | ECHO);
tcsetattr(fileno(in), TCSANOW, &term);
const char *cmd = "\033[6n";
write(fileno(out), cmd, strlen(cmd));
char buf[64];
read(fileno(in), buf, sizeof buf);
tcsetattr(fileno(in), TCSANOW, &restore);
unsigned int row, col;
int r = sscanf(buf, "\033[%u;%uR", &row, &col);
if (r != 2) {
return -1;
}
*x = col - 1;
*y = row - 1;
return 0;
}
int z__fx_stream_set_modifier(FILE *fp, enum z__fx_stream_modifier mod)
{
char buf[128];
int buf_i = 0;
int nr_codes = 0;
buf[buf_i++] = '\033';
buf[buf_i++] = '[';
if (mod & Z__FX_STREAM_MOD_RESET) {
buf[buf_i++] = '0';
buf[buf_i++] = 'm';
buf[buf_i++] = '\0';
fputs(buf, fp);
return 0;
}
if (mod & Z__FX_STREAM_MOD_BOLD) {
if (nr_codes > 0) {
buf[buf_i++] = ';';
}
buf[buf_i++] = '1';
nr_codes++;
}
if (mod & Z__FX_STREAM_MOD_ITALIC) {
if (nr_codes > 0) {
buf[buf_i++] = ';';
}
buf[buf_i++] = '3';
nr_codes++;
}
if (mod & Z__FX_STREAM_MOD_ULINE) {
if (nr_codes > 0) {
buf[buf_i++] = ';';
}
buf[buf_i++] = '4';
nr_codes++;
}
if (mod & Z__FX_STREAM_MOD_INVERT) {
if (nr_codes > 0) {
buf[buf_i++] = ';';
}
buf[buf_i++] = '7';
nr_codes++;
}
if (Z__FX_STREAM_MOD_GET_FG_COLOUR(mod) != 0) {
if (nr_codes > 0) {
buf[buf_i++] = ';';
}
buf[buf_i++] = mod & Z__FX_STREAM_MOD_BRIGHT ? '9' : '3';
}
switch (Z__FX_STREAM_MOD_GET_FG_COLOUR(mod)) {
case Z__FX_STREAM_MOD_BLACK:
buf[buf_i++] = '0';
nr_codes++;
break;
case Z__FX_STREAM_MOD_RED:
buf[buf_i++] = '1';
nr_codes++;
break;
case Z__FX_STREAM_MOD_GREEN:
buf[buf_i++] = '2';
nr_codes++;
break;
case Z__FX_STREAM_MOD_FX:
buf[buf_i++] = '4';
nr_codes++;
break;
case Z__FX_STREAM_MOD_RED | Z__FX_STREAM_MOD_GREEN:
buf[buf_i++] = '3';
nr_codes++;
break;
case Z__FX_STREAM_MOD_RED | Z__FX_STREAM_MOD_FX:
buf[buf_i++] = '5';
nr_codes++;
break;
case Z__FX_STREAM_MOD_GREEN | Z__FX_STREAM_MOD_FX:
buf[buf_i++] = '6';
nr_codes++;
break;
case Z__FX_STREAM_MOD_RED | Z__FX_STREAM_MOD_GREEN | Z__FX_STREAM_MOD_FX:
buf[buf_i++] = '7';
nr_codes++;
break;
default:
break;
}
if (Z__FX_STREAM_MOD_GET_BG_COLOUR(mod) != 0) {
if (nr_codes > 0) {
buf[buf_i++] = ';';
}
if (mod & Z__FX_STREAM_MOD_BG_BRIGHT) {
buf[buf_i++] = '1';
buf[buf_i++] = '0';
} else {
buf[buf_i++] = '9';
}
}
switch (Z__FX_STREAM_MOD_GET_BG_COLOUR(mod)) {
case Z__FX_STREAM_MOD_BG_BLACK:
buf[buf_i++] = '0';
nr_codes++;
break;
case Z__FX_STREAM_MOD_BG_RED:
buf[buf_i++] = '1';
nr_codes++;
break;
case Z__FX_STREAM_MOD_BG_GREEN:
buf[buf_i++] = '2';
nr_codes++;
break;
case Z__FX_STREAM_MOD_BG_FX:
buf[buf_i++] = '4';
nr_codes++;
break;
case Z__FX_STREAM_MOD_BG_RED | Z__FX_STREAM_MOD_BG_GREEN:
buf[buf_i++] = '3';
nr_codes++;
break;
case Z__FX_STREAM_MOD_BG_RED | Z__FX_STREAM_MOD_BG_FX:
buf[buf_i++] = '5';
nr_codes++;
break;
case Z__FX_STREAM_MOD_BG_GREEN | Z__FX_STREAM_MOD_BG_FX:
buf[buf_i++] = '6';
nr_codes++;
break;
case Z__FX_STREAM_MOD_BG_RED | Z__FX_STREAM_MOD_BG_GREEN
| Z__FX_STREAM_MOD_BG_FX:
buf[buf_i++] = '7';
nr_codes++;
break;
default:
break;
}
buf[buf_i++] = 'm';
buf[buf_i++] = '\0';
fputs(buf, fp);
return 0;
}
+552
View File
@@ -0,0 +1,552 @@
#include "../../tty.h"
#include <fx/term/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 {
FX_TTY_INIT = 0x01u,
FX_TTY_INTERACTIVE = 0x02u,
};
struct fx_tty {
FILE *t_in, *t_out;
enum tty_flags t_flags;
struct termios t_ios_raw, t_ios_default;
struct fx_tty_vmode t_vmode;
unsigned char t_mcount;
struct tty_format_buf t_format_buf;
};
static struct fx_tty std = {0};
static struct fx_tty err = {0};
static const char *ansi_colour16_fg[] = {
[FX_TTY_COLOUR16_BLACK] = "30",
[FX_TTY_COLOUR16_RED] = "31",
[FX_TTY_COLOUR16_GREEN] = "32",
[FX_TTY_COLOUR16_YELLOW] = "33",
[FX_TTY_COLOUR16_FX] = "34",
[FX_TTY_COLOUR16_MAGENTA] = "35",
[FX_TTY_COLOUR16_CYAN] = "36",
[FX_TTY_COLOUR16_WHITE] = "37",
[FX_TTY_COLOUR16_BRIGHT_BLACK] = "90",
[FX_TTY_COLOUR16_BRIGHT_RED] = "91",
[FX_TTY_COLOUR16_BRIGHT_GREEN] = "92",
[FX_TTY_COLOUR16_BRIGHT_YELLOW] = "93",
[FX_TTY_COLOUR16_BRIGHT_FX] = "94",
[FX_TTY_COLOUR16_BRIGHT_MAGENTA] = "95",
[FX_TTY_COLOUR16_BRIGHT_CYAN] = "96",
[FX_TTY_COLOUR16_BRIGHT_WHITE] = "97",
};
static const char *ansi_colour16_bg[] = {
[FX_TTY_COLOUR16_BLACK] = "40",
[FX_TTY_COLOUR16_RED] = "41",
[FX_TTY_COLOUR16_GREEN] = "42",
[FX_TTY_COLOUR16_YELLOW] = "43",
[FX_TTY_COLOUR16_FX] = "44",
[FX_TTY_COLOUR16_MAGENTA] = "45",
[FX_TTY_COLOUR16_CYAN] = "46",
[FX_TTY_COLOUR16_WHITE] = "47",
[FX_TTY_COLOUR16_BRIGHT_BLACK] = "100",
[FX_TTY_COLOUR16_BRIGHT_RED] = "101",
[FX_TTY_COLOUR16_BRIGHT_GREEN] = "102",
[FX_TTY_COLOUR16_BRIGHT_YELLOW] = "103",
[FX_TTY_COLOUR16_BRIGHT_FX] = "104",
[FX_TTY_COLOUR16_BRIGHT_MAGENTA] = "105",
[FX_TTY_COLOUR16_BRIGHT_CYAN] = "106",
[FX_TTY_COLOUR16_BRIGHT_WHITE] = "107",
};
static void init_tty(struct fx_tty *tty, FILE *in, FILE *out)
{
tty->t_in = in;
tty->t_out = out;
int fd = -1;
if (in) {
fd = fileno(in);
} else if (out) {
fd = fileno(out);
}
if (fd == -1) {
return;
}
if (isatty(fd)) {
tty->t_flags |= FX_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 |= FX_TTY_INIT;
}
struct fx_tty *z__fx_tty_get_std(void)
{
if (!(std.t_flags & FX_TTY_INIT)) {
init_tty(&std, stdin, stdout);
}
return &std;
}
struct fx_tty *z__fx_tty_get_err(void)
{
if (!(err.t_flags & FX_TTY_INIT)) {
init_tty(&err, NULL, stderr);
}
return &err;
}
struct tty_format_buf *z__fx_tty_get_format_buf(struct fx_tty *tty)
{
return &tty->t_format_buf;
}
void z__fx_tty_putc(struct fx_tty *tty, char c)
{
fputc(c, tty->t_out);
}
bool fx_tty_is_interactive(const struct fx_tty *tty)
{
return (tty->t_flags & FX_TTY_INTERACTIVE) == FX_TTY_INTERACTIVE;
}
static void set_raw(struct fx_tty *tty)
{
int fd = -1;
if (tty->t_in) {
fd = fileno(tty->t_in);
} else if (tty->t_out) {
fd = fileno(tty->t_out);
}
if (fd == -1) {
return;
}
tcsetattr(fd, TCSAFLUSH, &tty->t_ios_raw);
}
static void set_canon(struct fx_tty *tty)
{
int fd = -1;
if (tty->t_in) {
fd = fileno(tty->t_in);
} else if (tty->t_out) {
fd = fileno(tty->t_out);
}
if (fd == -1) {
return;
}
tcsetattr(fd, TCSAFLUSH, &tty->t_ios_default);
}
void fx_tty_set_mode(struct fx_tty *tty, enum fx_tty_mode mode)
{
switch (mode) {
case FX_TTY_CANONICAL:
set_canon(tty);
break;
case FX_TTY_RAW:
set_raw(tty);
break;
default:
break;
}
}
void fx_tty_reset_vmode(struct fx_tty *tty)
{
if (tty->t_vmode.v_fg.c_mode == FX_TTY_COLOUR_NONE
&& tty->t_vmode.v_bg.c_mode == FX_TTY_COLOUR_NONE
&& tty->t_vmode.v_attrib == FX_TTY_ATTRIB_NORMAL) {
return;
}
fprintf(tty->t_out, "\033[0m");
memset(&tty->t_vmode, 0x0, sizeof tty->t_vmode);
tty->t_vmode.v_fg.c_mode = FX_TTY_COLOUR_NONE;
tty->t_vmode.v_bg.c_mode = FX_TTY_COLOUR_NONE;
tty->t_vmode.v_attrib = FX_TTY_ATTRIB_NORMAL;
}
static int compare_colour(
const struct fx_tty_colour *a, const struct fx_tty_colour *b)
{
if (a->c_mode != b->c_mode) {
return -1;
}
switch (a->c_mode) {
case FX_TTY_COLOUR_16:
if (a->c_16.value != b->c_16.value) {
return -1;
}
break;
case FX_TTY_COLOUR_256:
if (a->c_256.value != b->c_256.value) {
return -1;
}
break;
case FX_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 fx_tty_vmode *a, const struct fx_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 fx_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 fx_tty *tty, enum fx_tty_attrib old, enum fx_tty_attrib new)
{
if (old == new) {
return;
}
/* Bold ON */
if (!(old & FX_TTY_ATTRIB_BOLD) && new &FX_TTY_ATTRIB_BOLD) {
put_ansi_attrib(tty, ANSI_BOLD_ON);
}
/* Bold OFF */
if (old & FX_TTY_ATTRIB_BOLD && !(new &FX_TTY_ATTRIB_BOLD)) {
put_ansi_attrib(tty, ANSI_BOLD_OFF);
}
/* Underline ON */
if (!(old & FX_TTY_ATTRIB_UNDERLINE) && new &FX_TTY_ATTRIB_UNDERLINE) {
put_ansi_attrib(tty, ANSI_UNDERLINE_ON);
}
/* Underline OFF */
if (old & FX_TTY_ATTRIB_UNDERLINE && !(new &FX_TTY_ATTRIB_UNDERLINE)) {
put_ansi_attrib(tty, ANSI_UNDERLINE_OFF);
}
/* Italic ON */
if (!(old & FX_TTY_ATTRIB_ITALIC) && new &FX_TTY_ATTRIB_ITALIC) {
put_ansi_attrib(tty, ANSI_ITALIC_ON);
}
/* Italic OFF */
if (old & FX_TTY_ATTRIB_ITALIC && !(new &FX_TTY_ATTRIB_ITALIC)) {
put_ansi_attrib(tty, ANSI_ITALIC_OFF);
}
}
static void set_fg(
struct fx_tty *tty, const struct fx_tty_colour *old,
const struct fx_tty_colour *new)
{
if (compare_colour(old, new) == 0) {
return;
}
char buf[8];
switch (new->c_mode) {
case FX_TTY_COLOUR_NONE:
put_ansi_attrib(tty, ANSI_DEFAULTCOLOUR_FG);
break;
case FX_TTY_COLOUR_16:
put_ansi_attrib(tty, ansi_colour16_fg[new->c_16.value]);
break;
case FX_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 FX_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 fx_tty *tty, const struct fx_tty_colour *old,
const struct fx_tty_colour *new)
{
if (compare_colour(old, new) == 0) {
return;
}
char buf[8];
switch (new->c_mode) {
case FX_TTY_COLOUR_NONE:
put_ansi_attrib(tty, ANSI_DEFAULTCOLOUR_BG);
break;
case FX_TTY_COLOUR_16:
put_ansi_attrib(tty, ansi_colour16_bg[new->c_16.value]);
break;
case FX_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 FX_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 fx_tty_set_vmode(struct fx_tty *tty, const struct fx_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);
}
fx_keycode fx_tty_read_key(struct fx_tty *tty)
{
char c;
int v;
int fd = fileno(tty->t_in);
while (1) {
v = read(fd, &c, 1);
if (v < 1) {
return FX_KEY_EOF;
}
if (c == '\r' || c == '\n') {
return FX_KEY_RETURN;
}
if (c == '\b' || c == 0x7F) {
return FX_KEY_BACKSPACE;
}
if (c >= 1 && c <= 26) {
return FX_TTY_CTRL_KEY(c + 'a' - 1);
}
if (c != 0x1b) {
return c;
}
v = read(fd, &c, 1);
if (v < 1) {
return FX_KEY_EOF;
}
if (c != '[') {
continue;
}
v = read(fd, &c, 1);
if (v < 1) {
return FX_KEY_EOF;
}
switch (c) {
case 'A':
return FX_KEY_ARROW_UP;
case 'B':
return FX_KEY_ARROW_DOWN;
case 'C':
return FX_KEY_ARROW_RIGHT;
case 'D':
return FX_KEY_ARROW_LEFT;
default:
continue;
}
}
return c;
}
void fx_tty_move_cursor_x(struct fx_tty *tty, enum fx_tty_position_base base, int pos)
{
if (base == FX_TTY_POS_CURSOR && pos < 0 && pos >= -4) {
for (int i = 0; i > pos; i--) {
fputc('\b', tty->t_out);
}
return;
}
if (base == FX_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 fx_tty_move_cursor_y(struct fx_tty *tty, enum fx_tty_position_base base, int pos)
{
if (base == FX_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 fx_tty_clear(struct fx_tty *tty, enum fx_tty_clear_mode mode)
{
const char *arg;
if (mode & FX_TTY_CLEAR_ALL) {
arg = "2";
} else if (mode & FX_TTY_CLEAR_TO_CURSOR) {
arg = "1";
} else if (mode & FX_TTY_CLEAR_FROM_CURSOR) {
arg = "";
} else {
abort();
}
if (mode & FX_TTY_CLEAR_SCREEN) {
fprintf(tty->t_out, "\033[%sJ", arg);
} else if (mode & FX_TTY_CLEAR_LINE) {
fprintf(tty->t_out, "\033[%sK", arg);
} else {
abort();
}
}
enum fx_status fx_tty_get_dimensions(
struct fx_tty *tty, unsigned int *w, unsigned int *h)
{
if (!(tty->t_flags & FX_TTY_INTERACTIVE)) {
return -1;
}
int fd = fileno(tty->t_out);
struct winsize ws;
if (ioctl(fd, TIOCGWINSZ, &ws) == -1) {
return -1;
}
if (w) {
*w = ws.ws_col;
}
if (h) {
*h = ws.ws_row;
}
return 0;
}
+224
View File
@@ -0,0 +1,224 @@
#define _POSIX_C_SOURCE 200809L
#include "../../print.h"
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
int z__fx_stream_is_tty(FILE *fp)
{
return isatty(fileno(fp));
}
int z__fx_stream_dimensions(FILE *fp, unsigned int *w, unsigned int *h)
{
if (!isatty(fileno(fp))) {
return -1;
}
struct winsize ws;
if (ioctl(fileno(fp), TIOCGWINSZ, &ws) == -1) {
return -1;
}
if (w) {
*w = ws.ws_col;
}
if (h) {
*h = ws.ws_row;
}
return 0;
}
int z__fx_stream_cursorpos(FILE *in, FILE *out, unsigned int *x, unsigned int *y)
{
if (!isatty(fileno(in)) || !isatty(fileno(out))) {
return -1;
}
struct termios term, restore;
tcgetattr(fileno(in), &term);
tcgetattr(fileno(in), &restore);
term.c_lflag &= ~(ICANON | ECHO);
tcsetattr(fileno(in), TCSANOW, &term);
const char *cmd = "\033[6n";
write(fileno(out), cmd, strlen(cmd));
char buf[64];
read(fileno(in), buf, sizeof buf);
tcsetattr(fileno(in), TCSANOW, &restore);
unsigned int row, col;
int r = sscanf(buf, "\033[%u;%uR", &row, &col);
if (r != 2) {
return -1;
}
*x = col - 1;
*y = row - 1;
return 0;
}
int z__fx_stream_set_modifier(FILE *fp, enum z__fx_stream_modifier mod)
{
char buf[128];
int buf_i = 0;
int nr_codes = 0;
buf[buf_i++] = '\033';
buf[buf_i++] = '[';
if (mod & Z__FX_STREAM_MOD_RESET) {
buf[buf_i++] = '0';
buf[buf_i++] = 'm';
buf[buf_i++] = '\0';
fputs(buf, fp);
return 0;
}
if (mod & Z__FX_STREAM_MOD_BOLD) {
if (nr_codes > 0) {
buf[buf_i++] = ';';
}
buf[buf_i++] = '1';
nr_codes++;
}
if (mod & Z__FX_STREAM_MOD_ITALIC) {
if (nr_codes > 0) {
buf[buf_i++] = ';';
}
buf[buf_i++] = '3';
nr_codes++;
}
if (mod & Z__FX_STREAM_MOD_ULINE) {
if (nr_codes > 0) {
buf[buf_i++] = ';';
}
buf[buf_i++] = '4';
nr_codes++;
}
if (mod & Z__FX_STREAM_MOD_INVERT) {
if (nr_codes > 0) {
buf[buf_i++] = ';';
}
buf[buf_i++] = '7';
nr_codes++;
}
if (Z__FX_STREAM_MOD_GET_FG_COLOUR(mod) != 0) {
if (nr_codes > 0) {
buf[buf_i++] = ';';
}
buf[buf_i++] = mod & Z__FX_STREAM_MOD_BRIGHT ? '9' : '3';
}
switch (Z__FX_STREAM_MOD_GET_FG_COLOUR(mod)) {
case Z__FX_STREAM_MOD_BLACK:
buf[buf_i++] = '0';
nr_codes++;
break;
case Z__FX_STREAM_MOD_RED:
buf[buf_i++] = '1';
nr_codes++;
break;
case Z__FX_STREAM_MOD_GREEN:
buf[buf_i++] = '2';
nr_codes++;
break;
case Z__FX_STREAM_MOD_FX:
nr_codes++;
break;
case Z__FX_STREAM_MOD_RED | Z__FX_STREAM_MOD_GREEN:
buf[buf_i++] = '3';
nr_codes++;
break;
case Z__FX_STREAM_MOD_RED | Z__FX_STREAM_MOD_FX:
buf[buf_i++] = '5';
nr_codes++;
break;
case Z__FX_STREAM_MOD_GREEN | Z__FX_STREAM_MOD_FX:
buf[buf_i++] = '6';
nr_codes++;
break;
case Z__FX_STREAM_MOD_RED | Z__FX_STREAM_MOD_GREEN | Z__FX_STREAM_MOD_FX:
buf[buf_i++] = '7';
nr_codes++;
break;
default:
break;
}
if (Z__FX_STREAM_MOD_GET_BG_COLOUR(mod) != 0) {
if (nr_codes > 0) {
buf[buf_i++] = ';';
}
if (mod & Z__FX_STREAM_MOD_BG_BRIGHT) {
buf[buf_i++] = '1';
buf[buf_i++] = '0';
} else {
buf[buf_i++] = '9';
}
}
switch (Z__FX_STREAM_MOD_GET_BG_COLOUR(mod)) {
case Z__FX_STREAM_MOD_BG_BLACK:
buf[buf_i++] = '0';
nr_codes++;
break;
case Z__FX_STREAM_MOD_BG_RED:
buf[buf_i++] = '1';
nr_codes++;
break;
case Z__FX_STREAM_MOD_BG_GREEN:
buf[buf_i++] = '2';
nr_codes++;
break;
case Z__FX_STREAM_MOD_BG_FX:
nr_codes++;
break;
case Z__FX_STREAM_MOD_BG_RED | Z__FX_STREAM_MOD_BG_GREEN:
buf[buf_i++] = '3';
nr_codes++;
break;
case Z__FX_STREAM_MOD_BG_RED | Z__FX_STREAM_MOD_BG_FX:
buf[buf_i++] = '5';
nr_codes++;
break;
case Z__FX_STREAM_MOD_BG_GREEN | Z__FX_STREAM_MOD_BG_FX:
buf[buf_i++] = '6';
nr_codes++;
break;
case Z__FX_STREAM_MOD_BG_RED | Z__FX_STREAM_MOD_BG_GREEN
| Z__FX_STREAM_MOD_BG_FX:
buf[buf_i++] = '7';
nr_codes++;
break;
default:
break;
}
buf[buf_i++] = 'm';
buf[buf_i++] = '\0';
fputs(buf, fp);
return 0;
}
+554
View File
@@ -0,0 +1,554 @@
#define _POSIX_C_SOURCE 200809L
#include "../../tty.h"
#include <fx/term/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 {
FX_TTY_INIT = 0x01u,
FX_TTY_INTERACTIVE = 0x02u,
};
struct fx_tty {
FILE *t_in, *t_out;
enum tty_flags t_flags;
struct termios t_ios_raw, t_ios_default;
struct fx_tty_vmode t_vmode;
unsigned char t_mcount;
struct tty_format_buf t_format_buf;
};
static struct fx_tty std = {0};
static struct fx_tty err = {0};
static const char *ansi_colour16_fg[] = {
[FX_TTY_COLOUR16_BLACK] = "30",
[FX_TTY_COLOUR16_RED] = "31",
[FX_TTY_COLOUR16_GREEN] = "32",
[FX_TTY_COLOUR16_YELLOW] = "33",
[FX_TTY_COLOUR16_FX] = "34",
[FX_TTY_COLOUR16_MAGENTA] = "35",
[FX_TTY_COLOUR16_CYAN] = "36",
[FX_TTY_COLOUR16_WHITE] = "37",
[FX_TTY_COLOUR16_BRIGHT_BLACK] = "90",
[FX_TTY_COLOUR16_BRIGHT_RED] = "91",
[FX_TTY_COLOUR16_BRIGHT_GREEN] = "92",
[FX_TTY_COLOUR16_BRIGHT_YELLOW] = "93",
[FX_TTY_COLOUR16_BRIGHT_FX] = "94",
[FX_TTY_COLOUR16_BRIGHT_MAGENTA] = "95",
[FX_TTY_COLOUR16_BRIGHT_CYAN] = "96",
[FX_TTY_COLOUR16_BRIGHT_WHITE] = "97",
};
static const char *ansi_colour16_bg[] = {
[FX_TTY_COLOUR16_BLACK] = "40",
[FX_TTY_COLOUR16_RED] = "41",
[FX_TTY_COLOUR16_GREEN] = "42",
[FX_TTY_COLOUR16_YELLOW] = "43",
[FX_TTY_COLOUR16_FX] = "44",
[FX_TTY_COLOUR16_MAGENTA] = "45",
[FX_TTY_COLOUR16_CYAN] = "46",
[FX_TTY_COLOUR16_WHITE] = "47",
[FX_TTY_COLOUR16_BRIGHT_BLACK] = "100",
[FX_TTY_COLOUR16_BRIGHT_RED] = "101",
[FX_TTY_COLOUR16_BRIGHT_GREEN] = "102",
[FX_TTY_COLOUR16_BRIGHT_YELLOW] = "103",
[FX_TTY_COLOUR16_BRIGHT_FX] = "104",
[FX_TTY_COLOUR16_BRIGHT_MAGENTA] = "105",
[FX_TTY_COLOUR16_BRIGHT_CYAN] = "106",
[FX_TTY_COLOUR16_BRIGHT_WHITE] = "107",
};
static void init_tty(struct fx_tty *tty, FILE *in, FILE *out)
{
tty->t_in = in;
tty->t_out = out;
int fd = -1;
if (in) {
fd = fileno(in);
} else if (out) {
fd = fileno(out);
}
if (fd == -1) {
return;
}
if (isatty(fd)) {
tty->t_flags |= FX_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 |= FX_TTY_INIT;
}
struct fx_tty *z__fx_tty_get_std(void)
{
if (!(std.t_flags & FX_TTY_INIT)) {
init_tty(&std, stdin, stdout);
}
return &std;
}
struct fx_tty *z__fx_tty_get_err(void)
{
if (!(err.t_flags & FX_TTY_INIT)) {
init_tty(&err, NULL, stderr);
}
return &err;
}
struct tty_format_buf *z__fx_tty_get_format_buf(struct fx_tty *tty)
{
return &tty->t_format_buf;
}
void z__fx_tty_putc(struct fx_tty *tty, char c)
{
fputc(c, tty->t_out);
}
bool fx_tty_is_interactive(const struct fx_tty *tty)
{
return (tty->t_flags & FX_TTY_INTERACTIVE) == FX_TTY_INTERACTIVE;
}
static void set_raw(struct fx_tty *tty)
{
int fd = -1;
if (tty->t_in) {
fd = fileno(tty->t_in);
} else if (tty->t_out) {
fd = fileno(tty->t_out);
}
if (fd == -1) {
return;
}
tcsetattr(fd, TCSAFLUSH, &tty->t_ios_raw);
}
static void set_canon(struct fx_tty *tty)
{
int fd = -1;
if (tty->t_in) {
fd = fileno(tty->t_in);
} else if (tty->t_out) {
fd = fileno(tty->t_out);
}
if (fd == -1) {
return;
}
tcsetattr(fd, TCSAFLUSH, &tty->t_ios_default);
}
void fx_tty_set_mode(struct fx_tty *tty, enum fx_tty_mode mode)
{
switch (mode) {
case FX_TTY_CANONICAL:
set_canon(tty);
break;
case FX_TTY_RAW:
set_raw(tty);
break;
default:
break;
}
}
void fx_tty_reset_vmode(struct fx_tty *tty)
{
if (tty->t_vmode.v_fg.c_mode == FX_TTY_COLOUR_NONE
&& tty->t_vmode.v_bg.c_mode == FX_TTY_COLOUR_NONE
&& tty->t_vmode.v_attrib == FX_TTY_ATTRIB_NORMAL) {
return;
}
fprintf(tty->t_out, "\033[0m");
memset(&tty->t_vmode, 0x0, sizeof tty->t_vmode);
tty->t_vmode.v_fg.c_mode = FX_TTY_COLOUR_NONE;
tty->t_vmode.v_bg.c_mode = FX_TTY_COLOUR_NONE;
tty->t_vmode.v_attrib = FX_TTY_ATTRIB_NORMAL;
}
static int compare_colour(
const struct fx_tty_colour *a, const struct fx_tty_colour *b)
{
if (a->c_mode != b->c_mode) {
return -1;
}
switch (a->c_mode) {
case FX_TTY_COLOUR_16:
if (a->c_16.value != b->c_16.value) {
return -1;
}
break;
case FX_TTY_COLOUR_256:
if (a->c_256.value != b->c_256.value) {
return -1;
}
break;
case FX_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 fx_tty_vmode *a, const struct fx_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 fx_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 fx_tty *tty, enum fx_tty_attrib old, enum fx_tty_attrib new)
{
if (old == new) {
return;
}
/* Bold ON */
if (!(old & FX_TTY_ATTRIB_BOLD) && new &FX_TTY_ATTRIB_BOLD) {
put_ansi_attrib(tty, ANSI_BOLD_ON);
}
/* Bold OFF */
if (old & FX_TTY_ATTRIB_BOLD && !(new &FX_TTY_ATTRIB_BOLD)) {
put_ansi_attrib(tty, ANSI_BOLD_OFF);
}
/* Underline ON */
if (!(old & FX_TTY_ATTRIB_UNDERLINE) && new &FX_TTY_ATTRIB_UNDERLINE) {
put_ansi_attrib(tty, ANSI_UNDERLINE_ON);
}
/* Underline OFF */
if (old & FX_TTY_ATTRIB_UNDERLINE && !(new &FX_TTY_ATTRIB_UNDERLINE)) {
put_ansi_attrib(tty, ANSI_UNDERLINE_OFF);
}
/* Italic ON */
if (!(old & FX_TTY_ATTRIB_ITALIC) && new &FX_TTY_ATTRIB_ITALIC) {
put_ansi_attrib(tty, ANSI_ITALIC_ON);
}
/* Italic OFF */
if (old & FX_TTY_ATTRIB_ITALIC && !(new &FX_TTY_ATTRIB_ITALIC)) {
put_ansi_attrib(tty, ANSI_ITALIC_OFF);
}
}
static void set_fg(
struct fx_tty *tty, const struct fx_tty_colour *old,
const struct fx_tty_colour *new)
{
if (compare_colour(old, new) == 0) {
return;
}
char buf[8];
switch (new->c_mode) {
case FX_TTY_COLOUR_NONE:
put_ansi_attrib(tty, ANSI_DEFAULTCOLOUR_FG);
break;
case FX_TTY_COLOUR_16:
put_ansi_attrib(tty, ansi_colour16_fg[new->c_16.value]);
break;
case FX_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 FX_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 fx_tty *tty, const struct fx_tty_colour *old,
const struct fx_tty_colour *new)
{
if (compare_colour(old, new) == 0) {
return;
}
char buf[8];
switch (new->c_mode) {
case FX_TTY_COLOUR_NONE:
put_ansi_attrib(tty, ANSI_DEFAULTCOLOUR_BG);
break;
case FX_TTY_COLOUR_16:
put_ansi_attrib(tty, ansi_colour16_bg[new->c_16.value]);
break;
case FX_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 FX_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 fx_tty_set_vmode(struct fx_tty *tty, const struct fx_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);
}
fx_keycode fx_tty_read_key(struct fx_tty *tty)
{
char c;
int v;
int fd = fileno(tty->t_in);
while (1) {
v = read(fd, &c, 1);
if (v < 1) {
return FX_KEY_EOF;
}
if (c == '\r' || c == '\n') {
return FX_KEY_RETURN;
}
if (c == '\b' || c == 0x7F) {
return FX_KEY_BACKSPACE;
}
if (c >= 1 && c <= 26) {
return FX_TTY_CTRL_KEY(c + 'a' - 1);
}
if (c != 0x1b) {
return c;
}
v = read(fd, &c, 1);
if (v < 1) {
return FX_KEY_EOF;
}
if (c != '[') {
continue;
}
v = read(fd, &c, 1);
if (v < 1) {
return FX_KEY_EOF;
}
switch (c) {
case 'A':
return FX_KEY_ARROW_UP;
case 'B':
return FX_KEY_ARROW_DOWN;
case 'C':
return FX_KEY_ARROW_RIGHT;
case 'D':
return FX_KEY_ARROW_LEFT;
default:
continue;
}
}
return c;
}
void fx_tty_move_cursor_x(struct fx_tty *tty, enum fx_tty_position_base base, int pos)
{
if (base == FX_TTY_POS_CURSOR && pos < 0 && pos >= -4) {
for (int i = 0; i > pos; i--) {
fputc('\b', tty->t_out);
}
return;
}
if (base == FX_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 fx_tty_move_cursor_y(struct fx_tty *tty, enum fx_tty_position_base base, int pos)
{
if (base == FX_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 fx_tty_clear(struct fx_tty *tty, enum fx_tty_clear_mode mode)
{
const char *arg;
if (mode & FX_TTY_CLEAR_ALL) {
arg = "2";
} else if (mode & FX_TTY_CLEAR_TO_CURSOR) {
arg = "1";
} else if (mode & FX_TTY_CLEAR_FROM_CURSOR) {
arg = "";
} else {
abort();
}
if (mode & FX_TTY_CLEAR_SCREEN) {
fprintf(tty->t_out, "\033[%sJ", arg);
} else if (mode & FX_TTY_CLEAR_LINE) {
fprintf(tty->t_out, "\033[%sK", arg);
} else {
abort();
}
}
enum fx_status fx_tty_get_dimensions(
struct fx_tty *tty, unsigned int *w, unsigned int *h)
{
if (!(tty->t_flags & FX_TTY_INTERACTIVE)) {
return -1;
}
int fd = fileno(tty->t_out);
struct winsize ws;
if (ioctl(fd, TIOCGWINSZ, &ws) == -1) {
return -1;
}
if (w) {
*w = ws.ws_col;
}
if (h) {
*h = ws.ws_row;
}
return 0;
}
+133
View File
@@ -0,0 +1,133 @@
#include "../../print.h"
#include <Windows.h>
#include <stdio.h>
#include <string.h>
int z__fx_stream_is_tty(FILE *fp)
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
HANDLE console = (HANDLE)(INT_PTR)_get_osfhandle(fileno(fp));
BOOL status = GetConsoleScreenBufferInfo(console, &csbi);
return status == TRUE ? 1 : 0;
}
int z__fx_stream_dimensions(FILE *fp, unsigned int *w, unsigned int *h)
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
HANDLE console = (HANDLE)(INT_PTR)_get_osfhandle(fileno(fp));
BOOL status = GetConsoleScreenBufferInfo(console, &csbi);
if (status == FALSE) {
return -1;
}
if (w) {
*w = csbi.srWindow.Right - csbi.srWindow.Left;
}
if (h) {
*h = csbi.srWindow.Bottom - csbi.srWindow.Top;
}
return 0;
}
int z__fx_stream_cursorpos(FILE *in, FILE *out, unsigned int *x, unsigned int *y)
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
HANDLE console = (HANDLE)(INT_PTR)_get_osfhandle(fileno(in));
BOOL status = GetConsoleScreenBufferInfo(console, &csbi);
if (status == FALSE) {
return -1;
}
if (x) {
*x = csbi.dwCursorPosition.X;
}
if (y) {
*y = csbi.dwCursorPosition.Y;
}
return 0;
}
#define APPLY_FLAG(mod, attrib, x, y) \
if ((mod) & (x)) { \
(attrib) |= (y); \
}
#define APPLY_FLAG_X(mod, attrib, x, y) \
if ((mod) & (x)) { \
(attrib) |= (y); \
} else { \
(attrib) &= ~(y); \
}
int z__fx_stream_set_modifier(FILE *fp, unsigned int mod)
{
WORD attrib = 0;
CONSOLE_SCREEN_BUFFER_INFO csbi;
HANDLE console = (HANDLE)(INT_PTR)_get_osfhandle(fileno(fp));
BOOL status = GetConsoleScreenBufferInfo(console, &csbi);
if (status == FALSE) {
return -1;
}
attrib = csbi.wAttributes;
if (mod & Z__FX_STREAM_MOD_RESET) {
attrib = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_FX;
SetConsoleTextAttribute(console, attrib);
return 0;
}
if (Z__FX_STREAM_MOD_GET_FG_COLOUR(mod) != 0) {
if (mod & Z__FX_STREAM_MOD_BLACK) {
attrib
&= ~(FOREGROUND_RED | FOREGROUND_GREEN
| FOREGROUND_FX);
} else {
APPLY_FLAG_X(
mod, attrib, Z__FX_STREAM_MOD_RED, FOREGROUND_RED);
APPLY_FLAG_X(
mod, attrib, Z__FX_STREAM_MOD_GREEN,
FOREGROUND_GREEN);
APPLY_FLAG_X(
mod, attrib, Z__FX_STREAM_MOD_FX, FOREGROUND_FX);
}
}
if (Z__FX_STREAM_MOD_GET_BG_COLOUR(mod) != 0) {
if (mod & Z__FX_STREAM_MOD_BG_BLACK) {
attrib
&= ~(BACKGROUND_RED | BACKGROUND_GREEN
| BACKGROUND_FX);
} else {
APPLY_FLAG_X(
mod, attrib, Z__FX_STREAM_MOD_BG_RED,
BACKGROUND_RED);
APPLY_FLAG_X(
mod, attrib, Z__FX_STREAM_MOD_BG_GREEN,
BACKGROUND_GREEN);
APPLY_FLAG_X(
mod, attrib, Z__FX_STREAM_MOD_BG_FX,
BACKGROUND_FX);
}
}
APPLY_FLAG(mod, attrib, Z__FX_STREAM_MOD_ULINE, COMMON_LVB_UNDERSCORE);
APPLY_FLAG(mod, attrib, Z__FX_STREAM_MOD_INVERT, COMMON_LVB_REVERSE_VIDEO);
APPLY_FLAG(
mod, attrib, Z__FX_STREAM_MOD_BRIGHT | Z__FX_STREAM_MOD_BOLD,
FOREGROUND_INTENSITY);
APPLY_FLAG(mod, attrib, Z__FX_STREAM_MOD_BG_BRIGHT, BACKGROUND_INTENSITY);
SetConsoleTextAttribute(console, attrib);
return 0;
}
+570
View File
@@ -0,0 +1,570 @@
#include <fx/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 {
FX_TTY_INIT = 0x01u,
FX_TTY_INTERACTIVE = 0x02u,
};
struct fx_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 fx_tty_vmode t_vmode;
struct tty_format_buf t_format_buf;
};
static struct fx_tty std = {0};
static struct fx_tty err = {0};
static WORD ansi_colour16_fg[] = {
[FX_TTY_COLOUR16_BLACK] = 0,
[FX_TTY_COLOUR16_RED] = FOREGROUND_RED,
[FX_TTY_COLOUR16_GREEN] = FOREGROUND_GREEN,
[FX_TTY_COLOUR16_YELLOW] = FOREGROUND_RED | FOREGROUND_GREEN,
[FX_TTY_COLOUR16_FX] = FOREGROUND_FX,
[FX_TTY_COLOUR16_MAGENTA] = FOREGROUND_RED | FOREGROUND_FX,
[FX_TTY_COLOUR16_CYAN] = FOREGROUND_GREEN | FOREGROUND_FX,
[FX_TTY_COLOUR16_WHITE] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_FX,
[FX_TTY_COLOUR16_BRIGHT_BLACK] = FOREGROUND_INTENSITY,
[FX_TTY_COLOUR16_BRIGHT_RED] = FOREGROUND_RED | FOREGROUND_INTENSITY,
[FX_TTY_COLOUR16_BRIGHT_GREEN] = FOREGROUND_GREEN | FOREGROUND_INTENSITY,
[FX_TTY_COLOUR16_BRIGHT_YELLOW] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY,
[FX_TTY_COLOUR16_BRIGHT_FX] = FOREGROUND_FX | FOREGROUND_INTENSITY,
[FX_TTY_COLOUR16_BRIGHT_MAGENTA] = FOREGROUND_RED | FOREGROUND_FX | FOREGROUND_INTENSITY,
[FX_TTY_COLOUR16_BRIGHT_CYAN] = FOREGROUND_GREEN | FOREGROUND_FX | FOREGROUND_INTENSITY,
[FX_TTY_COLOUR16_BRIGHT_WHITE] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_FX | FOREGROUND_INTENSITY,
};
static WORD ansi_colour16_bg[] = {
[FX_TTY_COLOUR16_BLACK] = 0,
[FX_TTY_COLOUR16_RED] = BACKGROUND_RED,
[FX_TTY_COLOUR16_GREEN] = BACKGROUND_GREEN,
[FX_TTY_COLOUR16_YELLOW] = BACKGROUND_RED | BACKGROUND_GREEN,
[FX_TTY_COLOUR16_FX] = BACKGROUND_FX,
[FX_TTY_COLOUR16_MAGENTA] = BACKGROUND_RED | BACKGROUND_FX,
[FX_TTY_COLOUR16_CYAN] = BACKGROUND_GREEN | BACKGROUND_FX,
[FX_TTY_COLOUR16_WHITE] = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_FX,
[FX_TTY_COLOUR16_BRIGHT_BLACK] = BACKGROUND_INTENSITY,
[FX_TTY_COLOUR16_BRIGHT_RED] = BACKGROUND_RED | BACKGROUND_INTENSITY,
[FX_TTY_COLOUR16_BRIGHT_GREEN] = BACKGROUND_GREEN | BACKGROUND_INTENSITY,
[FX_TTY_COLOUR16_BRIGHT_YELLOW] = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_INTENSITY,
[FX_TTY_COLOUR16_BRIGHT_FX] = BACKGROUND_FX | BACKGROUND_INTENSITY,
[FX_TTY_COLOUR16_BRIGHT_MAGENTA] = BACKGROUND_RED | BACKGROUND_FX | BACKGROUND_INTENSITY,
[FX_TTY_COLOUR16_BRIGHT_CYAN] = BACKGROUND_GREEN | BACKGROUND_FX | BACKGROUND_INTENSITY,
[FX_TTY_COLOUR16_BRIGHT_WHITE] = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_FX | BACKGROUND_INTENSITY,
};
static void init_tty(struct fx_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 |= FX_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 |= FX_TTY_INTERACTIVE;
}
if (out && GetConsoleScreenBufferInfo(out_handle, &csbi)) {
GetConsoleMode(out_handle, &mode);
tty->t_canon_mode_out = mode;
tty->t_flags |= FX_TTY_INTERACTIVE;
}
}
struct fx_tty *z__fx_tty_get_std(void)
{
if (!(std.t_flags & FX_TTY_INIT)) {
init_tty(&std, stdin, stdout);
}
return &std;
}
struct fx_tty *z__fx_tty_get_err(void)
{
if (!(err.t_flags & FX_TTY_INIT)) {
init_tty(&err, NULL, stderr);
}
return &err;
}
struct tty_format_buf *z__fx_tty_get_format_buf(struct fx_tty *tty)
{
return &tty->t_format_buf;
}
void z__fx_tty_putc(struct fx_tty* tty, char c)
{
DWORD x;
WriteConsoleA(tty->t_out, &c, 1, &x, NULL);
}
bool fx_tty_is_interactive(const struct fx_tty *tty)
{
return (tty->t_flags & FX_TTY_INTERACTIVE) == FX_TTY_INTERACTIVE;
}
static void tty_set_raw(struct fx_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 fx_tty *tty)
{
SetConsoleMode(tty->t_in, tty->t_canon_mode_in);
SetConsoleMode(tty->t_out, tty->t_canon_mode_out);
}
void fx_tty_set_mode(struct fx_tty* tty, enum fx_tty_mode mode)
{
switch (mode) {
case FX_TTY_CANONICAL:
tty_set_canon(tty);
break;
case FX_TTY_RAW:
tty_set_raw(tty);
break;
default:
break;
}
}
void fx_tty_reset_vmode(struct fx_tty *tty)
{
if (tty->t_vmode.v_fg.c_mode == FX_TTY_COLOUR_NONE
&& tty->t_vmode.v_bg.c_mode == FX_TTY_COLOUR_NONE
&& tty->t_vmode.v_attrib == FX_TTY_ATTRIB_NORMAL) {
return;
}
WORD attrib = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_FX;
SetConsoleTextAttribute(tty->t_out, attrib);
memset(&tty->t_vmode, 0x0, sizeof tty->t_vmode);
tty->t_vmode.v_fg.c_mode = FX_TTY_COLOUR_NONE;
tty->t_vmode.v_bg.c_mode = FX_TTY_COLOUR_NONE;
tty->t_vmode.v_attrib = FX_TTY_ATTRIB_NORMAL;
}
static int compare_colour(
const struct fx_tty_colour *a, const struct fx_tty_colour *b)
{
if (a->c_mode != b->c_mode) {
return -1;
}
switch (a->c_mode) {
case FX_TTY_COLOUR_16:
if (a->c_16.value != b->c_16.value) {
return -1;
}
break;
case FX_TTY_COLOUR_256:
if (a->c_256.value != b->c_256.value) {
return -1;
}
break;
case FX_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 fx_tty_vmode *a, const struct fx_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 fx_tty_attrib old, enum fx_tty_attrib new)
{
if (old == new) {
return;
}
WORD attrib = *attrp;
/* Bold ON */
if (!(old & FX_TTY_ATTRIB_BOLD) && new & FX_TTY_ATTRIB_BOLD) {
attrib |= FOREGROUND_INTENSITY;
}
/* Bold OFF */
if (old & FX_TTY_ATTRIB_BOLD && !(new & FX_TTY_ATTRIB_BOLD)) {
attrib &= ~FOREGROUND_INTENSITY;
}
/* Underline ON */
if (!(old & FX_TTY_ATTRIB_UNDERLINE) && new & FX_TTY_ATTRIB_UNDERLINE) {
attrib |= COMMON_LVB_UNDERSCORE;
}
/* Underline OFF */
if (old & FX_TTY_ATTRIB_UNDERLINE && !(new & FX_TTY_ATTRIB_UNDERLINE)) {
attrib &= ~COMMON_LVB_UNDERSCORE;
}
/* Italic ON */
if (!(old & FX_TTY_ATTRIB_ITALIC) && new & FX_TTY_ATTRIB_ITALIC) {
/* not supported */
}
/* Italic OFF */
if (old & FX_TTY_ATTRIB_ITALIC && !(new & FX_TTY_ATTRIB_ITALIC)) {
/* not supported */
}
*attrp = attrib;
}
static void set_fg(
WORD *attrp, const struct fx_tty_colour *old,
const struct fx_tty_colour *new)
{
if (compare_colour(old, new) == 0) {
return;
}
WORD attrib = *attrp;
attrib &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_FX | FOREGROUND_INTENSITY);
switch (new->c_mode) {
case FX_TTY_COLOUR_16:
attrib |= ansi_colour16_fg[new->c_16.value];
break;
default:
attrib |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_FX;
break;
}
*attrp = attrib;
}
static void set_bg(
WORD *attrp, const struct fx_tty_colour *old,
const struct fx_tty_colour *new)
{
if (compare_colour(old, new) == 0) {
return;
}
WORD attrib = *attrp;
attrib &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_FX | BACKGROUND_INTENSITY);
switch (new->c_mode) {
case FX_TTY_COLOUR_16:
attrib |= ansi_colour16_bg[new->c_16.value];
break;
default:
attrib |= BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_FX;
break;
}
}
void fx_tty_set_vmode(struct fx_tty *tty, const struct fx_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 fx_status fx_tty_get_dimensions(struct fx_tty* tty, unsigned int* w, unsigned int* h)
{
if (!(tty->t_flags & FX_TTY_INTERACTIVE)) {
return FX_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 FX_SUCCESS;
}
enum fx_status fx_tty_get_cursor_position(struct fx_tty *tty, unsigned int *x, unsigned int *y)
{
if (!(tty->t_flags & FX_TTY_INTERACTIVE)) {
return FX_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 FX_SUCCESS;
}
fx_keycode fx_tty_read_key(struct fx_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 FX_KEY_EOF;
}
if (d.EventType != KEY_EVENT) {
continue;
}
if (!d.Event.KeyEvent.bKeyDown) {
continue;
}
fx_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 = FX_KEY_ARROW_UP;
break;
case VK_DOWN:
key = FX_KEY_ARROW_DOWN;
break;
case VK_LEFT:
key = FX_KEY_ARROW_LEFT;
break;
case VK_RIGHT:
key = FX_KEY_ARROW_RIGHT;
break;
case VK_BACK:
key = FX_KEY_BACKSPACE;
break;
case VK_RETURN:
key = FX_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 = FX_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 FX_KEY_EOF;
}
void fx_tty_move_cursor_x(
struct fx_tty* tty, enum fx_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 FX_TTY_POS_CURSOR:
cursor_pos.X = console.dwCursorPosition.X + pos;
break;
case FX_TTY_POS_START:
cursor_pos.X = pos;
break;
default:
break;
}
SetConsoleCursorPosition(tty->t_out, cursor_pos);
}
void fx_tty_move_cursor_y(struct fx_tty *tty, enum fx_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 FX_TTY_POS_CURSOR:
cursor_pos.Y = console.dwCursorPosition.Y + pos;
break;
case FX_TTY_POS_START:
cursor_pos.Y = pos;
break;
default:
break;
}
SetConsoleCursorPosition(tty->t_out, cursor_pos);
}
void fx_tty_clear(struct fx_tty *tty, enum fx_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 & FX_TTY_CLEAR_ALL) {
line_length = console.dwSize.X;
all_length = line_length * console.dwSize.Y;
} else if (mode & FX_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 & FX_TTY_CLEAR_TO_CURSOR) {
line_length = console.dwCursorPosition.X;
all_length = line_length
+ ((console.dwCursorPosition.Y - 1) * console.dwSize.X);
} else {
abort();
}
if (mode & FX_TTY_CLEAR_SCREEN) {
length = all_length;
if ((mode & FX_TTY_CLEAR_ALL) || (mode & FX_TTY_CLEAR_TO_CURSOR)) {
start.X = 0;
start.Y = 0;
} else if (mode & FX_TTY_CLEAR_FROM_CURSOR) {
start = console.dwCursorPosition;
}
} else if (mode & FX_TTY_CLEAR_LINE) {
length = line_length;
if ((mode & FX_TTY_CLEAR_ALL) || (mode & FX_TTY_CLEAR_TO_CURSOR)) {
start.X = 0;
start.Y = console.dwCursorPosition.Y;
} else if (mode & FX_TTY_CLEAR_FROM_CURSOR) {
start = console.dwCursorPosition;
}
} else {
abort();
}
DWORD nr_written = 0;
FillConsoleOutputCharacterW(tty->t_out, fill, length, start, &nr_written);
}
+350
View File
@@ -0,0 +1,350 @@
#include "tty.h"
#include <fx/core/hash.h>
#include <fx/term/tty.h>
#include <string.h>
#define MOD_HASH_BLACK 0x4b5dd0abbc6fc1e4
#define MOD_HASH_RED 0x89e9be1960f4c21c
#define MOD_HASH_GREEN 0x0f40f029637fecbc
#define MOD_HASH_YELLOW 0x8346a574925e75a9
#define MOD_HASH_FX 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_FX 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_FX 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_FX 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 = fx_hash_cstr(modifier);
if (COMPARE_MOD_NAME(modifier, "black", mod_hash, MOD_HASH_BLACK)) {
fmt->vmode.v_fg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = FX_TTY_COLOUR16_BLACK;
}
if (COMPARE_MOD_NAME(modifier, "red", mod_hash, MOD_HASH_RED)) {
fmt->vmode.v_fg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = FX_TTY_COLOUR16_RED;
}
if (COMPARE_MOD_NAME(modifier, "green", mod_hash, MOD_HASH_GREEN)) {
fmt->vmode.v_fg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = FX_TTY_COLOUR16_GREEN;
}
if (COMPARE_MOD_NAME(modifier, "yellow", mod_hash, MOD_HASH_YELLOW)) {
fmt->vmode.v_fg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = FX_TTY_COLOUR16_YELLOW;
}
if (COMPARE_MOD_NAME(modifier, "blue", mod_hash, MOD_HASH_FX)) {
fmt->vmode.v_fg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = FX_TTY_COLOUR16_FX;
}
if (COMPARE_MOD_NAME(modifier, "magenta", mod_hash, MOD_HASH_MAGENTA)) {
fmt->vmode.v_fg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = FX_TTY_COLOUR16_MAGENTA;
}
if (COMPARE_MOD_NAME(modifier, "cyan", mod_hash, MOD_HASH_CYAN)) {
fmt->vmode.v_fg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = FX_TTY_COLOUR16_CYAN;
}
if (COMPARE_MOD_NAME(modifier, "white", mod_hash, MOD_HASH_WHITE)) {
fmt->vmode.v_fg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = FX_TTY_COLOUR16_WHITE;
}
if (COMPARE_MOD_NAME(modifier, "dark_grey", mod_hash, MOD_HASH_DARK_GREY)) {
fmt->vmode.v_fg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = FX_TTY_COLOUR16_BRIGHT_BLACK;
}
if (COMPARE_MOD_NAME(modifier, "bright_red", mod_hash, MOD_HASH_BRIGHT_RED)) {
fmt->vmode.v_fg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = FX_TTY_COLOUR16_BRIGHT_RED;
}
if (COMPARE_MOD_NAME(
modifier, "bright_green", mod_hash, MOD_HASH_BRIGHT_GREEN)) {
fmt->vmode.v_fg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = FX_TTY_COLOUR16_BRIGHT_GREEN;
}
if (COMPARE_MOD_NAME(
modifier, "bright_yellow", mod_hash, MOD_HASH_BRIGHT_YELLOW)) {
fmt->vmode.v_fg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = FX_TTY_COLOUR16_BRIGHT_YELLOW;
}
if (COMPARE_MOD_NAME(
modifier, "bright_blue", mod_hash, MOD_HASH_BRIGHT_FX)) {
fmt->vmode.v_fg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = FX_TTY_COLOUR16_BRIGHT_FX;
}
if (COMPARE_MOD_NAME(
modifier, "bright_magenta", mod_hash, MOD_HASH_BRIGHT_MAGENTA)) {
fmt->vmode.v_fg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = FX_TTY_COLOUR16_BRIGHT_MAGENTA;
}
if (COMPARE_MOD_NAME(
modifier, "bright_cyan", mod_hash, MOD_HASH_BRIGHT_CYAN)) {
fmt->vmode.v_fg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = FX_TTY_COLOUR16_BRIGHT_CYAN;
}
if (COMPARE_MOD_NAME(
modifier, "bright_white", mod_hash, MOD_HASH_BRIGHT_WHITE)) {
fmt->vmode.v_fg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_fg.c_16.value = FX_TTY_COLOUR16_BRIGHT_WHITE;
}
if (COMPARE_MOD_NAME(modifier, "bg_black", mod_hash, MOD_HASH_BG_BLACK)) {
fmt->vmode.v_bg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = FX_TTY_COLOUR16_BLACK;
}
if (COMPARE_MOD_NAME(modifier, "bg_red", mod_hash, MOD_HASH_BG_RED)) {
fmt->vmode.v_bg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = FX_TTY_COLOUR16_RED;
}
if (COMPARE_MOD_NAME(modifier, "bg_green", mod_hash, MOD_HASH_BG_GREEN)) {
fmt->vmode.v_bg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = FX_TTY_COLOUR16_GREEN;
}
if (COMPARE_MOD_NAME(modifier, "bg_yellow", mod_hash, MOD_HASH_BG_YELLOW)) {
fmt->vmode.v_bg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = FX_TTY_COLOUR16_YELLOW;
}
if (COMPARE_MOD_NAME(modifier, "bg_blue", mod_hash, MOD_HASH_BG_FX)) {
fmt->vmode.v_bg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = FX_TTY_COLOUR16_FX;
}
if (COMPARE_MOD_NAME(modifier, "bg_magenta", mod_hash, MOD_HASH_BG_MAGENTA)) {
fmt->vmode.v_bg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = FX_TTY_COLOUR16_MAGENTA;
}
if (COMPARE_MOD_NAME(modifier, "bg_cyan", mod_hash, MOD_HASH_BG_CYAN)) {
fmt->vmode.v_bg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = FX_TTY_COLOUR16_CYAN;
}
if (COMPARE_MOD_NAME(modifier, "bg_white", mod_hash, MOD_HASH_BG_WHITE)) {
fmt->vmode.v_bg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = FX_TTY_COLOUR16_WHITE;
}
if (COMPARE_MOD_NAME(
modifier, "bg_dark_grey", mod_hash, MOD_HASH_BG_DARK_GREY)) {
fmt->vmode.v_bg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = FX_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 = FX_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = FX_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 = FX_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = FX_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 = FX_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = FX_TTY_COLOUR16_BRIGHT_YELLOW;
}
if (COMPARE_MOD_NAME(
modifier, "bright_bg_blue", mod_hash, MOD_HASH_BRIGHT_BG_FX)) {
fmt->vmode.v_bg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = FX_TTY_COLOUR16_BRIGHT_FX;
}
if (COMPARE_MOD_NAME(
modifier, "bright_bg_magenta", mod_hash,
MOD_HASH_BRIGHT_BG_MAGENTA)) {
fmt->vmode.v_bg.c_mode = FX_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = FX_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 = FX_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = FX_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 = FX_TTY_COLOUR_16;
fmt->vmode.v_bg.c_16.value = FX_TTY_COLOUR16_BRIGHT_WHITE;
}
if (COMPARE_MOD_NAME(modifier, "bold", mod_hash, MOD_HASH_BOLD)) {
fmt->vmode.v_attrib |= FX_TTY_ATTRIB_BOLD;
}
if (COMPARE_MOD_NAME(modifier, "uline", mod_hash, MOD_HASH_ULINE)) {
fmt->vmode.v_attrib |= FX_TTY_ATTRIB_UNDERLINE;
}
if (COMPARE_MOD_NAME(modifier, "italic", mod_hash, MOD_HASH_ITALIC)) {
fmt->vmode.v_attrib |= FX_TTY_ATTRIB_ITALIC;
}
if (COMPARE_MOD_NAME(modifier, "invert", mod_hash, MOD_HASH_INVERT)) {
fmt->vmode.v_attrib |= FX_TTY_ATTRIB_INVERT;
}
if (COMPARE_MOD_NAME(modifier, "reset", mod_hash, MOD_HASH_RESET)) {
fmt->reset_vmode = true;
fmt->vmode.v_fg.c_mode = FX_TTY_COLOUR_NONE;
fmt->vmode.v_bg.c_mode = FX_TTY_COLOUR_NONE;
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';
fmt->reset_vmode = false;
}
static void flush_vmode(struct fx_tty *tty, struct tty_format_buf *fmt)
{
if (fmt->reset_vmode) {
fx_tty_reset_vmode(tty);
} else {
fx_tty_set_vmode(tty, &fmt->vmode);
}
}
int fx_tty_putc(struct fx_tty *tty, enum fx_tty_print_flags flags, char c)
{
if (flags & FX_TTY_DISABLE_FORMATTING) {
z__fx_tty_putc(tty, c);
return c;
}
struct tty_format_buf *fmt = z__fx_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__fx_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__fx_tty_putc(tty, c);
}
return c;
}
int fx_tty_puts(struct fx_tty *tty, enum fx_tty_print_flags flags, const char *s)
{
int r = 0;
while (s[r]) {
fx_tty_putc(tty, flags, s[r]);
r++;
}
return r;
}
int fx_tty_printf(struct fx_tty *tty, enum fx_tty_print_flags flags, const char *s, ...)
{
va_list arg;
va_start(arg, s);
int r = fx_tty_vprintf(tty, flags, s, arg);
va_end(arg);
return r;
}
+23
View File
@@ -0,0 +1,23 @@
#ifndef _TTY_H_
#define _TTY_H_
#include <fx/term/tty.h>
#include <stdbool.h>
#define TTY_TMPBUF_SIZE 128
struct fx_tty;
struct tty_format_buf {
bool in_format;
bool reset_vmode;
char buf[TTY_TMPBUF_SIZE];
unsigned int ptr;
struct fx_tty_vmode vmode;
};
extern struct tty_format_buf *z__fx_tty_get_format_buf(struct fx_tty *tty);
extern void z__fx_tty_putc(struct fx_tty *tty, char c);
#endif