mirror of
https://github.com/systemd/systemd.git
synced 2025-01-10 05:18:17 +03:00
Merge pull request #31162 from poettering/tint-tweaks
ptyfwd: some tweaks to terminal handling
This commit is contained in:
commit
27af7c2b4c
@ -74,6 +74,9 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
|
||||
[SPECIAL_GLYPH_SPARKLES] = "*",
|
||||
[SPECIAL_GLYPH_LOW_BATTERY] = "!",
|
||||
[SPECIAL_GLYPH_WARNING_SIGN] = "!",
|
||||
[SPECIAL_GLYPH_RED_CIRCLE] = "o",
|
||||
[SPECIAL_GLYPH_YELLOW_CIRCLE] = "o",
|
||||
[SPECIAL_GLYPH_BLUE_CIRCLE] = "o",
|
||||
},
|
||||
|
||||
/* UTF-8 */
|
||||
@ -136,6 +139,10 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
|
||||
[SPECIAL_GLYPH_WARNING_SIGN] = u8"⚠️",
|
||||
[SPECIAL_GLYPH_COMPUTER_DISK] = u8"💽",
|
||||
[SPECIAL_GLYPH_WORLD] = u8"🌍",
|
||||
|
||||
[SPECIAL_GLYPH_RED_CIRCLE] = u8"🔴",
|
||||
[SPECIAL_GLYPH_YELLOW_CIRCLE] = u8"🟡",
|
||||
[SPECIAL_GLYPH_BLUE_CIRCLE] = u8"🔵",
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -49,6 +49,9 @@ typedef enum SpecialGlyph {
|
||||
SPECIAL_GLYPH_WARNING_SIGN,
|
||||
SPECIAL_GLYPH_COMPUTER_DISK,
|
||||
SPECIAL_GLYPH_WORLD,
|
||||
SPECIAL_GLYPH_RED_CIRCLE,
|
||||
SPECIAL_GLYPH_YELLOW_CIRCLE,
|
||||
SPECIAL_GLYPH_BLUE_CIRCLE,
|
||||
_SPECIAL_GLYPH_MAX,
|
||||
_SPECIAL_GLYPH_INVALID = -EINVAL,
|
||||
} SpecialGlyph;
|
||||
|
@ -89,6 +89,10 @@
|
||||
/* Set cursor to top left corner and clear screen */
|
||||
#define ANSI_HOME_CLEAR "\x1B[H\x1B[2J"
|
||||
|
||||
/* Push/pop a window title off the stack of window titles */
|
||||
#define ANSI_WINDOW_TITLE_PUSH "\x1b[22;2t"
|
||||
#define ANSI_WINDOW_TITLE_POP "\x1b[23;2t"
|
||||
|
||||
bool isatty_safe(int fd);
|
||||
|
||||
int reset_terminal_fd(int fd, bool switch_to_text);
|
||||
|
@ -4429,6 +4429,22 @@ static int setup_notify_parent(sd_event *event, int fd, pid_t *inner_child_pid,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_window_title(PTYForward *f) {
|
||||
_cleanup_free_ char *hn = NULL, *dot = NULL;
|
||||
|
||||
assert(f);
|
||||
|
||||
(void) gethostname_strict(&hn);
|
||||
|
||||
if (emoji_enabled())
|
||||
dot = strjoin(special_glyph(SPECIAL_GLYPH_BLUE_CIRCLE), " ");
|
||||
|
||||
if (hn)
|
||||
(void) pty_forward_set_titlef(f, "%sContainer %s on %s", strempty(dot), arg_machine, hn);
|
||||
else
|
||||
(void) pty_forward_set_titlef(f, "%sContainer %s", strempty(dot), arg_machine);
|
||||
}
|
||||
|
||||
static int merge_settings(Settings *settings, const char *path) {
|
||||
int rl;
|
||||
|
||||
@ -5361,6 +5377,7 @@ static int run_container(
|
||||
} else if (!isempty(arg_background))
|
||||
(void) pty_forward_set_background_color(forward, arg_background);
|
||||
|
||||
set_window_title(forward);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "exit-status.h"
|
||||
#include "fd-util.h"
|
||||
#include "format-util.h"
|
||||
#include "hostname-util.h"
|
||||
#include "main-func.h"
|
||||
#include "parse-argument.h"
|
||||
#include "parse-util.h"
|
||||
@ -188,6 +189,13 @@ static int help_sudo_mode(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool privileged_execution(void) {
|
||||
if (arg_runtime_scope != RUNTIME_SCOPE_SYSTEM)
|
||||
return false;
|
||||
|
||||
return !arg_exec_user || STR_IN_SET(arg_exec_user, "root", "0");
|
||||
}
|
||||
|
||||
static int add_timer_property(const char *name, const char *val) {
|
||||
char *p;
|
||||
|
||||
@ -941,7 +949,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
|
||||
if (!arg_background && arg_stdio == ARG_STDIO_PTY) {
|
||||
double hue;
|
||||
|
||||
if (!arg_exec_user || STR_IN_SET(arg_exec_user, "root", "0"))
|
||||
if (privileged_execution())
|
||||
hue = 0; /* red */
|
||||
else
|
||||
hue = 60 /* yellow */;
|
||||
@ -1584,6 +1592,26 @@ static int acquire_invocation_id(sd_bus *bus, const char *unit, sd_id128_t *ret)
|
||||
return !sd_id128_is_null(*ret);
|
||||
}
|
||||
|
||||
static void set_window_title(PTYForward *f) {
|
||||
_cleanup_free_ char *hn = NULL, *cl = NULL, *dot = NULL;
|
||||
assert(f);
|
||||
|
||||
if (!arg_host)
|
||||
(void) gethostname_strict(&hn);
|
||||
|
||||
cl = strv_join(arg_cmdline, " ");
|
||||
if (!cl)
|
||||
return (void) log_oom();
|
||||
|
||||
if (emoji_enabled())
|
||||
dot = strjoin(special_glyph(privileged_execution() ? SPECIAL_GLYPH_RED_CIRCLE : SPECIAL_GLYPH_YELLOW_CIRCLE), " ");
|
||||
|
||||
if (arg_host || hn)
|
||||
(void) pty_forward_set_titlef(f, "%s%s on %s", strempty(dot), cl, arg_host ?: hn);
|
||||
else
|
||||
(void) pty_forward_set_titlef(f, "%s%s", strempty(dot), cl);
|
||||
}
|
||||
|
||||
static int start_transient_service(sd_bus *bus) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
@ -1739,6 +1767,8 @@ static int start_transient_service(sd_bus *bus) {
|
||||
|
||||
if (!isempty(arg_background))
|
||||
(void) pty_forward_set_background_color(c.forward, arg_background);
|
||||
|
||||
set_window_title(c.forward);
|
||||
}
|
||||
|
||||
path = unit_dbus_path_from_name(service);
|
||||
|
@ -34,6 +34,7 @@ typedef enum AnsiColorState {
|
||||
ANSI_COLOR_STATE_ESC,
|
||||
ANSI_COLOR_STATE_CSI_SEQUENCE,
|
||||
ANSI_COLOR_STATE_NEWLINE,
|
||||
ANSI_COLOR_STATE_CARRIAGE_RETURN,
|
||||
_ANSI_COLOR_STATE_MAX,
|
||||
_ANSI_COLOR_STATE_INVALID = -EINVAL,
|
||||
} AnsiColorState;
|
||||
@ -91,6 +92,8 @@ struct PTYForward {
|
||||
char *background_color;
|
||||
AnsiColorState ansi_color_state;
|
||||
char *csi_sequence;
|
||||
|
||||
char *title;
|
||||
};
|
||||
|
||||
#define ESCAPE_USEC (1*USEC_PER_SEC)
|
||||
@ -114,9 +117,13 @@ static void pty_forward_disconnect(PTYForward *f) {
|
||||
/* STDIN/STDOUT should not be non-blocking normally, so let's reset it */
|
||||
(void) fd_nonblock(f->output_fd, false);
|
||||
|
||||
if (colors_enabled())
|
||||
if (colors_enabled()) {
|
||||
(void) loop_write(f->output_fd, ANSI_NORMAL ANSI_ERASE_TO_END_OF_SCREEN, SIZE_MAX);
|
||||
|
||||
if (f->title)
|
||||
(void) loop_write(f->output_fd, ANSI_WINDOW_TITLE_POP, SIZE_MAX);
|
||||
}
|
||||
|
||||
if (f->close_output_fd)
|
||||
f->output_fd = safe_close(f->output_fd);
|
||||
}
|
||||
@ -230,9 +237,7 @@ static char *background_color_sequence(PTYForward *f) {
|
||||
assert(f);
|
||||
assert(f->background_color);
|
||||
|
||||
/* This sets the background color to the desired one, and erase the rest of the line with it */
|
||||
|
||||
return strjoin("\x1B[", f->background_color, "m", ANSI_ERASE_TO_END_OF_LINE);
|
||||
return strjoin("\x1B[", f->background_color, "m");
|
||||
}
|
||||
|
||||
static int insert_string(PTYForward *f, size_t offset, const char *s) {
|
||||
@ -257,12 +262,33 @@ static int insert_string(PTYForward *f, size_t offset, const char *s) {
|
||||
return (int) l;
|
||||
}
|
||||
|
||||
static int insert_erase_newline(PTYForward *f, size_t offset) {
|
||||
static int insert_newline_color_erase(PTYForward *f, size_t offset) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
|
||||
assert(f);
|
||||
assert(f->background_color);
|
||||
|
||||
/* When we see a newline (ASCII 10) then this sets the background color to the desired one, and erase the rest
|
||||
* of the line with it */
|
||||
|
||||
s = background_color_sequence(f);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!strextend(&s, ANSI_ERASE_TO_END_OF_LINE))
|
||||
return -ENOMEM;
|
||||
|
||||
return insert_string(f, offset, s);
|
||||
}
|
||||
|
||||
static int insert_carriage_return_color(PTYForward *f, size_t offset) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
|
||||
assert(f);
|
||||
assert(f->background_color);
|
||||
|
||||
/* When we see a carriage return (ASCII 13) this this sets only the background */
|
||||
|
||||
s = background_color_sequence(f);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
@ -363,37 +389,45 @@ static int pty_forward_ansi_process(PTYForward *f, size_t offset) {
|
||||
if (!f->background_color)
|
||||
return 0;
|
||||
|
||||
if (FLAGS_SET(f->flags, PTY_FORWARD_DUMB_TERMINAL))
|
||||
return 0;
|
||||
|
||||
for (size_t i = offset; i < f->out_buffer_full; i++) {
|
||||
char c = f->out_buffer[i];
|
||||
|
||||
switch (f->ansi_color_state) {
|
||||
|
||||
case ANSI_COLOR_STATE_TEXT:
|
||||
if (c == '\n')
|
||||
f->ansi_color_state = ANSI_COLOR_STATE_NEWLINE;
|
||||
if (c == 0x1B) /* ESC */
|
||||
f->ansi_color_state = ANSI_COLOR_STATE_ESC;
|
||||
break;
|
||||
|
||||
case ANSI_COLOR_STATE_NEWLINE: {
|
||||
/* Immediately after a newline insert an ANSI sequence to erase the line with a background color */
|
||||
|
||||
r = insert_erase_newline(f, i);
|
||||
r = insert_newline_color_erase(f, i);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
i += r;
|
||||
break;
|
||||
}
|
||||
|
||||
f->ansi_color_state = ANSI_COLOR_STATE_TEXT;
|
||||
case ANSI_COLOR_STATE_CARRIAGE_RETURN: {
|
||||
/* Immediately after a carriage return insert an ANSI sequence set the background color back */
|
||||
|
||||
r = insert_carriage_return_color(f, i);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
i += r;
|
||||
break;
|
||||
}
|
||||
|
||||
case ANSI_COLOR_STATE_ESC: {
|
||||
|
||||
if (c == '[')
|
||||
if (c == '[') {
|
||||
f->ansi_color_state = ANSI_COLOR_STATE_CSI_SEQUENCE;
|
||||
else
|
||||
f->ansi_color_state = ANSI_COLOR_STATE_TEXT;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
@ -408,7 +442,7 @@ static int pty_forward_ansi_process(PTYForward *f, size_t offset) {
|
||||
/* Safety check: lets not accept unbounded CSI sequences */
|
||||
|
||||
f->csi_sequence = mfree(f->csi_sequence);
|
||||
f->ansi_color_state = ANSI_COLOR_STATE_TEXT;
|
||||
break;
|
||||
} else if (!strextend(&f->csi_sequence, CHAR_TO_STR(c)))
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
@ -427,38 +461,65 @@ static int pty_forward_ansi_process(PTYForward *f, size_t offset) {
|
||||
f->ansi_color_state = ANSI_COLOR_STATE_TEXT;
|
||||
}
|
||||
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (c == '\n')
|
||||
f->ansi_color_state = ANSI_COLOR_STATE_NEWLINE;
|
||||
else if (c == '\r')
|
||||
f->ansi_color_state = ANSI_COLOR_STATE_CARRIAGE_RETURN;
|
||||
else if (c == 0x1B) /* ESC */
|
||||
f->ansi_color_state = ANSI_COLOR_STATE_ESC;
|
||||
else
|
||||
f->ansi_color_state = ANSI_COLOR_STATE_TEXT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int shovel(PTYForward *f) {
|
||||
static int do_shovel(PTYForward *f) {
|
||||
ssize_t k;
|
||||
int r;
|
||||
|
||||
assert(f);
|
||||
|
||||
if (f->out_buffer_size == 0 && f->background_color) {
|
||||
/* Erase the first line when we start */
|
||||
f->out_buffer = background_color_sequence(f);
|
||||
if (!f->out_buffer)
|
||||
return pty_forward_done(f, log_oom());
|
||||
if (f->out_buffer_size == 0 && !FLAGS_SET(f->flags, PTY_FORWARD_DUMB_TERMINAL)) {
|
||||
/* If the output hasn't been allocated yet, we are at the beginning of the first
|
||||
* shovelling. Hence, possibly send some initial ANSI sequences. But do so only if we are
|
||||
* talking to an actual TTY. */
|
||||
|
||||
f->out_buffer_full = strlen(f->out_buffer);
|
||||
f->out_buffer_size = MALLOC_SIZEOF_SAFE(f->out_buffer);
|
||||
if (f->background_color) {
|
||||
/* Erase the first line when we start */
|
||||
f->out_buffer = background_color_sequence(f);
|
||||
if (!f->out_buffer)
|
||||
return log_oom();
|
||||
|
||||
if (!strextend(&f->out_buffer, ANSI_ERASE_TO_END_OF_LINE))
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
if (f->title) {
|
||||
if (!strextend(&f->out_buffer,
|
||||
ANSI_WINDOW_TITLE_PUSH
|
||||
"\x1b]2;", f->title, "\a"))
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
if (f->out_buffer) {
|
||||
f->out_buffer_full = strlen(f->out_buffer);
|
||||
f->out_buffer_size = MALLOC_SIZEOF_SAFE(f->out_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (f->out_buffer_size < LINE_MAX) {
|
||||
/* Make sure we always have room for at least one "line" */
|
||||
void *p = realloc(f->out_buffer, LINE_MAX);
|
||||
if (!p)
|
||||
return pty_forward_done(f, log_oom());
|
||||
return log_oom();
|
||||
|
||||
f->out_buffer = p;
|
||||
f->out_buffer_size = MALLOC_SIZEOF_SAFE(p);
|
||||
@ -481,10 +542,8 @@ static int shovel(PTYForward *f) {
|
||||
f->stdin_hangup = true;
|
||||
|
||||
f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
|
||||
} else {
|
||||
log_error_errno(errno, "read(): %m");
|
||||
return pty_forward_done(f, -errno);
|
||||
}
|
||||
} else
|
||||
return log_error_errno(errno, "read(): %m");
|
||||
} else if (k == 0) {
|
||||
/* EOF on stdin */
|
||||
f->stdin_readable = false;
|
||||
@ -495,7 +554,7 @@ static int shovel(PTYForward *f) {
|
||||
/* Check if ^] has been pressed three times within one second. If we get this we quite
|
||||
* immediately. */
|
||||
if (look_for_escape(f, f->in_buffer + f->in_buffer_full, k))
|
||||
return pty_forward_done(f, -ECANCELED);
|
||||
return -ECANCELED;
|
||||
|
||||
f->in_buffer_full += (size_t) k;
|
||||
}
|
||||
@ -513,10 +572,8 @@ static int shovel(PTYForward *f) {
|
||||
f->master_hangup = true;
|
||||
|
||||
f->master_event_source = sd_event_source_unref(f->master_event_source);
|
||||
} else {
|
||||
log_error_errno(errno, "write(): %m");
|
||||
return pty_forward_done(f, -errno);
|
||||
}
|
||||
} else
|
||||
return log_error_errno(errno, "write(): %m");
|
||||
} else {
|
||||
assert(f->in_buffer_full >= (size_t) k);
|
||||
memmove(f->in_buffer, f->in_buffer + k, f->in_buffer_full - k);
|
||||
@ -540,10 +597,8 @@ static int shovel(PTYForward *f) {
|
||||
f->master_hangup = true;
|
||||
|
||||
f->master_event_source = sd_event_source_unref(f->master_event_source);
|
||||
} else {
|
||||
log_error_errno(errno, "read(): %m");
|
||||
return pty_forward_done(f, -errno);
|
||||
}
|
||||
} else
|
||||
return log_error_errno(errno, "read(): %m");
|
||||
} else {
|
||||
f->read_from_master = true;
|
||||
size_t scan_index = f->out_buffer_full;
|
||||
@ -551,7 +606,7 @@ static int shovel(PTYForward *f) {
|
||||
|
||||
r = pty_forward_ansi_process(f, scan_index);
|
||||
if (r < 0)
|
||||
return pty_forward_done(f, log_error_errno(r, "Failed to scan for ANSI sequences: %m"));
|
||||
return log_error_errno(r, "Failed to scan for ANSI sequences: %m");
|
||||
}
|
||||
}
|
||||
|
||||
@ -566,10 +621,8 @@ static int shovel(PTYForward *f) {
|
||||
f->stdout_writable = false;
|
||||
f->stdout_hangup = true;
|
||||
f->stdout_event_source = sd_event_source_unref(f->stdout_event_source);
|
||||
} else {
|
||||
log_error_errno(errno, "write(): %m");
|
||||
return pty_forward_done(f, -errno);
|
||||
}
|
||||
} else
|
||||
return log_error_errno(errno, "write(): %m");
|
||||
|
||||
} else {
|
||||
|
||||
@ -602,6 +655,18 @@ static int shovel(PTYForward *f) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int shovel(PTYForward *f) {
|
||||
int r;
|
||||
|
||||
assert(f);
|
||||
|
||||
r = do_shovel(f);
|
||||
if (r < 0)
|
||||
return pty_forward_done(f, r);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int on_master_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) {
|
||||
PTYForward *f = ASSERT_PTR(userdata);
|
||||
|
||||
@ -734,6 +799,10 @@ int pty_forward_new(
|
||||
|
||||
f->master = master;
|
||||
|
||||
/* Disable color/window title setting unless we talk to a good TTY */
|
||||
if (!isatty_safe(f->output_fd) || get_color_mode() == COLOR_OFF)
|
||||
f->flags |= PTY_FORWARD_DUMB_TERMINAL;
|
||||
|
||||
if (ioctl(f->output_fd, TIOCGWINSZ, &ws) < 0)
|
||||
/* If we can't get the resolution from the output fd, then use our internal, regular width/height,
|
||||
* i.e. something derived from $COLUMNS and $LINES if set. */
|
||||
@ -818,6 +887,7 @@ PTYForward *pty_forward_free(PTYForward *f) {
|
||||
return NULL;
|
||||
pty_forward_disconnect(f);
|
||||
free(f->background_color);
|
||||
free(f->title);
|
||||
return mfree(f);
|
||||
}
|
||||
|
||||
@ -958,3 +1028,35 @@ int pty_forward_set_background_color(PTYForward *f, const char *color) {
|
||||
|
||||
return free_and_strdup(&f->background_color, color);
|
||||
}
|
||||
|
||||
int pty_forward_set_title(PTYForward *f, const char *title) {
|
||||
assert(f);
|
||||
|
||||
/* Refuse accepting a title when we already started shoveling */
|
||||
if (f->out_buffer_size > 0)
|
||||
return -EBUSY;
|
||||
|
||||
return free_and_strdup(&f->title, title);
|
||||
}
|
||||
|
||||
int pty_forward_set_titlef(PTYForward *f, const char *format, ...) {
|
||||
_cleanup_free_ char *title = NULL;
|
||||
va_list ap;
|
||||
int r;
|
||||
|
||||
assert(f);
|
||||
assert(format);
|
||||
|
||||
if (f->out_buffer_size > 0)
|
||||
return -EBUSY;
|
||||
|
||||
va_start(ap, format);
|
||||
DISABLE_WARNING_FORMAT_NONLITERAL;
|
||||
r = vasprintf(&title, format, ap);
|
||||
REENABLE_WARNING;
|
||||
va_end(ap);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
return free_and_replace(f->title, title);
|
||||
}
|
||||
|
@ -10,13 +10,17 @@
|
||||
typedef struct PTYForward PTYForward;
|
||||
|
||||
typedef enum PTYForwardFlags {
|
||||
PTY_FORWARD_READ_ONLY = 1,
|
||||
/* Only output to STDOUT, never try to read from STDIN */
|
||||
PTY_FORWARD_READ_ONLY = 1 << 0,
|
||||
|
||||
/* Continue reading after hangup? */
|
||||
PTY_FORWARD_IGNORE_VHANGUP = 2,
|
||||
PTY_FORWARD_IGNORE_VHANGUP = 1 << 1,
|
||||
|
||||
/* Continue reading after hangup but only if we never read anything else? */
|
||||
PTY_FORWARD_IGNORE_INITIAL_VHANGUP = 4,
|
||||
PTY_FORWARD_IGNORE_INITIAL_VHANGUP = 1 << 2,
|
||||
|
||||
/* Don't tint the background, or set window title */
|
||||
PTY_FORWARD_DUMB_TERMINAL = 1 << 3,
|
||||
} PTYForwardFlags;
|
||||
|
||||
typedef int (*PTYForwardHandler)(PTYForward *f, int rcode, void *userdata);
|
||||
@ -40,5 +44,7 @@ int pty_forward_set_priority(PTYForward *f, int64_t priority);
|
||||
int pty_forward_set_width_height(PTYForward *f, unsigned width, unsigned height);
|
||||
|
||||
int pty_forward_set_background_color(PTYForward *f, const char *color);
|
||||
int pty_forward_set_title(PTYForward *f, const char *title);
|
||||
int pty_forward_set_titlef(PTYForward *f, const char *format, ...) _printf_(2,3);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(PTYForward*, pty_forward_free);
|
||||
|
@ -82,7 +82,7 @@ TEST(keymaps) {
|
||||
|
||||
#define dump_glyph(x) log_info(STRINGIFY(x) ": %s", special_glyph(x))
|
||||
TEST(dump_special_glyphs) {
|
||||
assert_cc(SPECIAL_GLYPH_WORLD + 1 == _SPECIAL_GLYPH_MAX);
|
||||
assert_cc(SPECIAL_GLYPH_BLUE_CIRCLE + 1 == _SPECIAL_GLYPH_MAX);
|
||||
|
||||
log_info("is_locale_utf8: %s", yes_no(is_locale_utf8()));
|
||||
|
||||
@ -127,6 +127,9 @@ TEST(dump_special_glyphs) {
|
||||
dump_glyph(SPECIAL_GLYPH_WARNING_SIGN);
|
||||
dump_glyph(SPECIAL_GLYPH_COMPUTER_DISK);
|
||||
dump_glyph(SPECIAL_GLYPH_WORLD);
|
||||
dump_glyph(SPECIAL_GLYPH_RED_CIRCLE);
|
||||
dump_glyph(SPECIAL_GLYPH_YELLOW_CIRCLE);
|
||||
dump_glyph(SPECIAL_GLYPH_BLUE_CIRCLE);
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_INFO);
|
||||
|
Loading…
Reference in New Issue
Block a user