From affa6f18c87eaf430cc0bf260c54fbfe32976a5f Mon Sep 17 00:00:00 2001 From: Dave Goodell Date: Mon, 24 Apr 2017 22:14:42 -0700 Subject: print_stream: add a new print_line_color() method This new interface allows printing a line with specified foreground and background colors. The implementation avoids printing escape sequences if the output stream is not a TTY and note that escape sequences are ignored on Windows. This initial implementation relies on relatively modern 24-bit color support which is present in many terminal emulators but may not always display properly on older or simpler emulators. Windows coloring is handled with SetConsoleTextAttribute, which offers a "1-bit" color experience (but it's better than nothing) This commit is a precursor to adding additional coloring to tshark. Bug: 5158 Change-Id: Ib2b9d800095a065a4bb60abe0550862cda5539ec Reviewed-on: https://code.wireshark.org/review/21324 Reviewed-by: Michael Mann --- epan/print_stream.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 143 insertions(+), 10 deletions(-) (limited to 'epan/print_stream.c') diff --git a/epan/print_stream.c b/epan/print_stream.c index da58960c04..02928fdd60 100644 --- a/epan/print_stream.c +++ b/epan/print_stream.c @@ -25,6 +25,7 @@ #include "config.h" #include +#include #ifdef _WIN32 #include @@ -38,6 +39,113 @@ #include +#define TERM_SGR_RESET "\x1B[0m" /* SGR - reset */ +#define TERM_CSI_EL "\x1B[K" /* EL - Erase in Line (to end of line) */ + +#ifdef _WIN32 +static void +print_color_escape(FILE *fh, const color_t *fg, const color_t *bg) +{ + /* default to white foreground, black background */ + WORD win_fg_color = FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN; + WORD win_bg_color = 0; + + /* Windows seems to offer 1-bit color, so you can't set the red, green, or blue intensities, + * you can only set "{foreground, background} contains {red, green, blue}". + * So include red, green or blue if the numeric intensity is high enough + */ + if (fg) { + if (((fg->red >> 8) & 0xff) >= 0x80) + { + win_fg_color |= FOREGROUND_RED; + } + else + { + win_fg_color &= (~FOREGROUND_RED); + } + if (((fg->green >> 8) & 0xff) >= 0x80) + { + win_fg_color |= FOREGROUND_GREEN; + } + else + { + win_fg_color &= (~FOREGROUND_GREEN); + } + if (((fg->blue >> 8) & 0xff) >= 0x80) + { + win_fg_color |= FOREGROUND_BLUE; + } + else + { + win_fg_color &= (~FOREGROUND_BLUE); + } + } + + if (bg) { + if (((bg->red >> 8) & 0xff) >= 0x80) + { + win_bg_color |= BACKGROUND_RED; + } + else + { + win_bg_color &= (~BACKGROUND_RED); + } + if (((bg->green >> 8) & 0xff) >= 0x80) + { + win_bg_color |= BACKGROUND_GREEN; + } + else + { + win_bg_color &= (~BACKGROUND_GREEN); + } + if (((bg->blue >> 8) & 0xff) >= 0x80) + { + win_bg_color |= BACKGROUND_BLUE; + } + else + { + win_bg_color &= (~BACKGROUND_BLUE); + } + } + + SetConsoleTextAttribute((HANDLE)_get_osfhandle(_fileno(fh)), win_fg_color|win_bg_color); +} +#else +static void +print_color_escape(FILE *fh, const color_t *fg, const color_t *bg) +{ + if (fg) { + /* + * emit 24-bit "true color" escape sequence if output is going to a + * tty, the sequence should be ignored by terminals that aren't capable + */ + fprintf(fh, "\x1B[38;2;%u;%u;%um", + (fg->red >> 8) & 0xff, + (fg->green >> 8) & 0xff, + (fg->blue >> 8) & 0xff); + } + + if (bg) { + fprintf(fh, "\x1B[48;2;%u;%u;%um", + (bg->red >> 8) & 0xff, + (bg->green >> 8) & 0xff, + (bg->blue >> 8) & 0xff); + } +} +#endif + +static void +print_color_eol(FILE *fh) +{ + /* + * Emit CSI EL to extend current background color all the way to EOL, + * otherwise we get a ragged right edge of color wherever the newline + * occurs. It's not perfect in every terminal emulator, but it generally + * works. + */ + fprintf(fh, "%s\n%s", TERM_CSI_EL, TERM_SGR_RESET); +} + static FILE * open_print_dest(gboolean to_file, const char *dest) { @@ -75,6 +183,15 @@ print_line(print_stream_t *self, int indent, const char *line) return (self->ops->print_line)(self, indent, line); } +gboolean +print_line_color(print_stream_t *self, int indent, const char *line, const color_t *fg, const color_t *bg) +{ + if (self->ops->print_line_color) + return (self->ops->print_line_color)(self, indent, line, fg, bg); + else + return (self->ops->print_line)(self, indent, line); +} + /* Insert bookmark */ gboolean print_bookmark(print_stream_t *self, const gchar *name, const gchar *title) @@ -108,21 +225,24 @@ typedef struct { #define MAX_INDENT 160 +/* returns TRUE if the print succeeded, FALSE if there was an error */ static gboolean -print_line_text(print_stream_t *self, int indent, const char *line) +print_line_color_text(print_stream_t *self, int indent, const char *line, const color_t *fg, const color_t *bg) { - static char spaces[MAX_INDENT]; + static char spaces[MAX_INDENT]; size_t ret; - output_text *output = (output_text *)self->data; unsigned int num_spaces; + gboolean emit_color = self->isatty && (fg != NULL || bg != NULL); /* should be space, if NUL -> initialize */ - if (!spaces[0]) { - int i; + if (!spaces[0]) + memset(spaces, ' ', sizeof(spaces)); - for (i = 0; i < MAX_INDENT; i++) - spaces[i] = ' '; + if (emit_color) { + print_color_escape(output->fh, fg, bg); + if (ferror(output->fh)) + return FALSE; } /* Prepare the tabs for printing, depending on tree level */ @@ -153,11 +273,22 @@ print_line_text(print_stream_t *self, int indent, const char *line) } else { fputs(line, output->fh); } - putc('\n', output->fh); + + if (emit_color) + print_color_eol(output->fh); + else + putc('\n', output->fh); } + return !ferror(output->fh); } +static gboolean +print_line_text(print_stream_t *self, int indent, const char *line) +{ + return print_line_color_text(self, indent, line, NULL, NULL); +} + static gboolean new_page_text(print_stream_t *self) { @@ -185,7 +316,8 @@ static const print_stream_ops_t print_text_ops = { NULL, /* bookmark */ new_page_text, NULL, /* finale */ - destroy_text + destroy_text, + print_line_color_text, }; static print_stream_t * @@ -368,7 +500,8 @@ static const print_stream_ops_t print_ps_ops = { print_bookmark_ps, new_page_ps, print_finale_ps, - destroy_ps + destroy_ps, + NULL, /* print_line_color */ }; static print_stream_t * -- cgit v1.2.3