#include "sd-event.h"
#include "term-internal.h"
#include "util.h"
+#include "utf8.h"
typedef struct Output Output;
typedef struct Terminal Terminal;
assert_return(o, -EINVAL);
r = ioctl(o->fd, TIOCGWINSZ, &wsz);
- if (r < 0) {
- log_error("error: cannot read window-size: %m");
- return -errno;
- }
+ if (r < 0)
+ return log_error_errno(errno, "error: cannot read window-size: %m");
if (wsz.ws_col != o->width || wsz.ws_row != o->height) {
o->width = wsz.ws_col;
}
static int output_flush(Output *o) {
- ssize_t len;
+ int r;
if (o->n_obuf < 1)
return 0;
- len = loop_write(o->fd, o->obuf, o->n_obuf, false);
- if (len < 0) {
- log_error("error: cannot write to TTY (%zd): %s", len, strerror(-len));
- return len;
- }
+ r = loop_write(o->fd, o->obuf, o->n_obuf, false);
+ if (r < 0)
+ return log_error_errno(r, "error: cannot write to TTY: %m");
o->n_obuf = 0;
return r;
len = loop_write(o->fd, buf, size, false);
- if (len < 0) {
- log_error("error: cannot write to TTY (%zd): %s", len, strerror(-len));
- return len;
- }
+ if (len < 0)
+ return log_error_errno(len, "error: cannot write to TTY (%zd): %m", len);
return 0;
}
+_printf_(3,0)
static int output_vnprintf(Output *o, size_t max, const char *format, va_list args) {
- char buf[4096];
+ char buf[max];
int r;
assert_return(o, -EINVAL);
assert_return(format, -EINVAL);
- assert_return(max <= sizeof(buf), -EINVAL);
+ assert_return(max <= 4096, -EINVAL);
- r = vsnprintf(buf, max, format, args);
- if (r > (ssize_t)max)
- r = max;
+ r = MIN(vsnprintf(buf, max, format, args), (int) max);
return output_write(o, buf, r);
}
+_printf_(3,4)
static int output_nprintf(Output *o, size_t max, const char *format, ...) {
va_list args;
int r;
return r;
}
+_printf_(2,0)
static int output_vprintf(Output *o, const char *format, va_list args) {
char buf[4096];
int r;
return output_write(o, buf, r);
}
+_printf_(2,3)
static int output_printf(Output *o, const char *format, ...) {
va_list args;
int r;
return r;
}
+_printf_(2,3)
static int output_frame_printl(Output *o, const char *format, ...) {
va_list args;
int r;
if (!o)
return NULL;
+ /* re-enable cursor */
+ output_printf(o, "\e[?25h");
/* disable alternate screen buffer */
output_printf(o, "\e[?1049l");
output_flush(o);
if (r < 0)
goto error;
+ /* always hide cursor */
+ r = output_printf(o, "\e[?25l");
+ if (r < 0)
+ goto error;
+
r = output_flush(o);
if (r < 0)
goto error;
static void output_draw_menu(Output *o) {
assert(o);
- output_frame_printl(o, "");
+ output_frame_printl(o, "%s", "");
output_frame_printl(o, " Menu: (the following keys are recognized)");
output_frame_printl(o, " q: quit");
output_frame_printl(o, " ^C: send ^C to the PTY");
}
-static void output_draw_screen(Output *o, term_screen *s) {
- unsigned int i, j;
- bool first = true;
-
- assert(o);
- assert(s);
-
- for (j = 0; j < s->page->height && j < o->in_height; ++j) {
- if (!first)
- output_printf(o, "\e[m\r\n" BORDER_VERT);
- first = false;
-
- for (i = 0; i < s->page->width && i < o->in_width; ++i) {
- term_charbuf_t buf;
- term_cell *cell = &s->page->lines[j]->cells[i];
- size_t k, len, ulen;
- const uint32_t *str;
- char utf8[4];
-
- switch (cell->attr.fg.ccode) {
- case TERM_CCODE_DEFAULT:
- output_printf(o, "\e[39m");
- break;
- case TERM_CCODE_256:
- output_printf(o, "\e[38;5;%um", cell->attr.fg.c256);
- break;
- case TERM_CCODE_RGB:
- output_printf(o, "\e[38;2;%u;%u;%um", cell->attr.fg.red, cell->attr.fg.green, cell->attr.fg.blue);
- break;
- case TERM_CCODE_BLACK ... TERM_CCODE_WHITE:
- if (cell->attr.bold)
- output_printf(o, "\e[%um", cell->attr.fg.ccode - TERM_CCODE_BLACK + 90);
- else
- output_printf(o, "\e[%um", cell->attr.fg.ccode - TERM_CCODE_BLACK + 30);
- break;
- case TERM_CCODE_LIGHT_BLACK ... TERM_CCODE_LIGHT_WHITE:
- output_printf(o, "\e[%um", cell->attr.fg.ccode - TERM_CCODE_LIGHT_BLACK + 90);
- break;
- }
+static int output_draw_cell_fn(term_screen *screen,
+ void *userdata,
+ unsigned int x,
+ unsigned int y,
+ const term_attr *attr,
+ const uint32_t *ch,
+ size_t n_ch,
+ unsigned int ch_width) {
+ Output *o = userdata;
+ size_t k, ulen;
+ char utf8[4];
+
+ if (x >= o->in_width || y >= o->in_height)
+ return 0;
- switch (cell->attr.bg.ccode) {
- case TERM_CCODE_DEFAULT:
- output_printf(o, "\e[49m");
- break;
- case TERM_CCODE_256:
- output_printf(o, "\e[48;5;%um", cell->attr.bg.c256);
- break;
- case TERM_CCODE_RGB:
- output_printf(o, "\e[48;2;%u;%u;%um", cell->attr.bg.red, cell->attr.bg.green, cell->attr.bg.blue);
- break;
- case TERM_CCODE_BLACK ... TERM_CCODE_WHITE:
- output_printf(o, "\e[%um", cell->attr.bg.ccode - TERM_CCODE_BLACK + 40);
- break;
- case TERM_CCODE_LIGHT_BLACK ... TERM_CCODE_LIGHT_WHITE:
- output_printf(o, "\e[%um", cell->attr.bg.ccode - TERM_CCODE_LIGHT_BLACK + 100);
- break;
- }
+ if (x == 0 && y != 0)
+ output_printf(o, "\e[m\r\n" BORDER_VERT);
- output_printf(o, "\e[%u;%u;%u;%u;%u;%um",
- cell->attr.bold ? 1 : 22,
- cell->attr.italic ? 3 : 23,
- cell->attr.underline ? 4 : 24,
- cell->attr.inverse ? 7 : 27,
- cell->attr.blink ? 5 : 25,
- cell->attr.hidden ? 8 : 28);
+ switch (attr->fg.ccode) {
+ case TERM_CCODE_DEFAULT:
+ output_printf(o, "\e[39m");
+ break;
+ case TERM_CCODE_256:
+ output_printf(o, "\e[38;5;%um", attr->fg.c256);
+ break;
+ case TERM_CCODE_RGB:
+ output_printf(o, "\e[38;2;%u;%u;%um", attr->fg.red, attr->fg.green, attr->fg.blue);
+ break;
+ case TERM_CCODE_BLACK ... TERM_CCODE_WHITE:
+ output_printf(o, "\e[%um", attr->fg.ccode - TERM_CCODE_BLACK + 30);
+ break;
+ case TERM_CCODE_LIGHT_BLACK ... TERM_CCODE_LIGHT_WHITE:
+ output_printf(o, "\e[%um", attr->fg.ccode - TERM_CCODE_LIGHT_BLACK + 90);
+ break;
+ }
- str = term_char_resolve(cell->ch, &len, &buf);
+ switch (attr->bg.ccode) {
+ case TERM_CCODE_DEFAULT:
+ output_printf(o, "\e[49m");
+ break;
+ case TERM_CCODE_256:
+ output_printf(o, "\e[48;5;%um", attr->bg.c256);
+ break;
+ case TERM_CCODE_RGB:
+ output_printf(o, "\e[48;2;%u;%u;%um", attr->bg.red, attr->bg.green, attr->bg.blue);
+ break;
+ case TERM_CCODE_BLACK ... TERM_CCODE_WHITE:
+ output_printf(o, "\e[%um", attr->bg.ccode - TERM_CCODE_BLACK + 40);
+ break;
+ case TERM_CCODE_LIGHT_BLACK ... TERM_CCODE_LIGHT_WHITE:
+ output_printf(o, "\e[%um", attr->bg.ccode - TERM_CCODE_LIGHT_BLACK + 100);
+ break;
+ }
- if (len < 1) {
- output_printf(o, " ");
- } else {
- for (k = 0; k < len; ++k) {
- ulen = term_utf8_encode(utf8, str[k]);
- output_write(o, utf8, ulen);
- }
- }
+ output_printf(o, "\e[%u;%u;%u;%u;%u;%um",
+ attr->bold ? 1 : 22,
+ attr->italic ? 3 : 23,
+ attr->underline ? 4 : 24,
+ attr->inverse ? 7 : 27,
+ attr->blink ? 5 : 25,
+ attr->hidden ? 8 : 28);
+
+ if (n_ch < 1) {
+ output_printf(o, " ");
+ } else {
+ for (k = 0; k < n_ch; ++k) {
+ ulen = utf8_encode_unichar(utf8, ch[k]);
+ output_write(o, utf8, ulen);
}
}
- output_move_to(o, s->cursor_x + 1, s->cursor_y + 3);
+ return 0;
+}
+
+static void output_draw_screen(Output *o, term_screen *s) {
+ assert(o);
+ assert(s);
+
+ term_screen_draw(s, output_draw_cell_fn, o, NULL);
+
output_printf(o, "\e[m");
}
else
output_draw_screen(o, screen);
- /* show cursor */
- if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
- output_printf(o, "\e[?25h");
-
/*
* Hack: sd-term was not written to support TTY as output-objects, thus
* expects callers to use term_screen_feed_keyboard(). However, we
*/
static void terminal_dirty(Terminal *t) {
- uint64_t usec;
+ usec_t usec;
int r;
assert(t);
if (t->pty) {
r = pty_resize(t->pty, t->output->in_width, t->output->in_height);
if (r < 0)
- log_error("error: pty_resize() (%d): %s", r, strerror(-r));
+ log_error_errno(r, "error: pty_resize() (%d): %m", r);
}
r = term_screen_resize(t->screen, t->output->in_width, t->output->in_height);
if (r < 0)
- log_error("error: term_screen_resize() (%d): %s", r, strerror(-r));
+ log_error_errno(r, "error: term_screen_resize() (%d): %m", r);
terminal_dirty(t);
assert(t);
- len = term_utf8_encode(buf, ucs4);
+ len = utf8_encode_unichar(buf, ucs4);
if (len < 1)
return 0;
if (t->pty) {
for (i = 0; i < num; ++i) {
r = pty_write(t->pty, vec[i].iov_base, vec[i].iov_len);
- if (r < 0) {
- log_error("error: cannot write to PTY (%d): %s", r, strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "error: cannot write to PTY (%d): %m", r);
}
}
if (errno == EAGAIN || errno == EINTR)
return 0;
- log_error("error: cannot read from TTY (%d): %m", -errno);
+ log_error_errno(errno, "error: cannot read from TTY (%d): %m", -errno);
return -errno;
}
for (i = 0; i < len; ++i) {
const term_seq *seq;
- const uint32_t *str;
+ uint32_t *str;
size_t n_str, j;
- str = term_utf8_decode(&t->utf8, &n_str, buf[i]);
+ n_str = term_utf8_decode(&t->utf8, &str, buf[i]);
for (j = 0; j < n_str; ++j) {
type = term_parser_feed(t->parser, &seq, str[j]);
- if (type < 0) {
- log_error("error: term_parser_feed() (%d): %s", type, strerror(-type));
- return type;
- }
+ if (type < 0)
+ return log_error_errno(type, "error: term_parser_feed() (%d): %m", type);
if (!t->is_menu) {
r = terminal_push_tmp(t, str[j]);
break;
case PTY_DATA:
r = term_screen_feed_text(t->screen, ptr, size);
- if (r < 0) {
- log_error("error: term_screen_feed_text() (%d): %s", r, strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "error: term_screen_feed_text() (%d): %m", r);
terminal_dirty(t);
break;
assert_return(out, -EINVAL);
r = tcgetattr(in_fd, &in_attr);
- if (r < 0) {
- log_error("error: tcgetattr() (%d): %m", -errno);
- return -errno;
- }
+ if (r < 0)
+ return log_error_errno(errno, "error: tcgetattr() (%d): %m", -errno);
r = tcgetattr(out_fd, &out_attr);
- if (r < 0) {
- log_error("error: tcgetattr() (%d): %m", -errno);
- return -errno;
- }
+ if (r < 0)
+ return log_error_errno(errno, "error: tcgetattr() (%d): %m", -errno);
t = new0(Terminal, 1);
if (!t)
r = tcsetattr(t->in_fd, TCSANOW, &in_attr);
if (r < 0) {
- log_error("error: tcsetattr() (%d): %s", r, strerror(-r));
+ log_error_errno(r, "error: tcsetattr() (%d): %m", r);
goto error;
}
r = tcsetattr(t->out_fd, TCSANOW, &out_attr);
if (r < 0) {
- log_error("error: tcsetattr() (%d): %s", r, strerror(-r));
+ log_error_errno(r, "error: tcsetattr() (%d): %m", r);
goto error;
}
r = sd_event_default(&t->event);
if (r < 0) {
- log_error("error: sd_event_default() (%d): %s", r, strerror(-r));
+ log_error_errno(r, "error: sd_event_default() (%d): %m", r);
goto error;
}
r = sigprocmask_many(SIG_BLOCK, SIGINT, SIGQUIT, SIGTERM, SIGWINCH, SIGCHLD, -1);
if (r < 0) {
- log_error("error: sigprocmask_many() (%d): %s", r, strerror(-r));
+ log_error_errno(r, "error: sigprocmask_many() (%d): %m", r);
goto error;
}
r = sd_event_add_signal(t->event, NULL, SIGINT, NULL, NULL);
if (r < 0) {
- log_error("error: sd_event_add_signal() (%d): %s", r, strerror(-r));
+ log_error_errno(r, "error: sd_event_add_signal() (%d): %m", r);
goto error;
}
r = sd_event_add_signal(t->event, NULL, SIGQUIT, NULL, NULL);
if (r < 0) {
- log_error("error: sd_event_add_signal() (%d): %s", r, strerror(-r));
+ log_error_errno(r, "error: sd_event_add_signal() (%d): %m", r);
goto error;
}
r = sd_event_add_signal(t->event, NULL, SIGTERM, NULL, NULL);
if (r < 0) {
- log_error("error: sd_event_add_signal() (%d): %s", r, strerror(-r));
+ log_error_errno(r, "error: sd_event_add_signal() (%d): %m", r);
goto error;
}
r = sd_event_add_signal(t->event, NULL, SIGWINCH, terminal_winch_fn, t);
if (r < 0) {
- log_error("error: sd_event_add_signal() (%d): %s", r, strerror(-r));
+ log_error_errno(r, "error: sd_event_add_signal() (%d): %m", r);
goto error;
}
t->is_dirty = true;
r = sd_event_add_time(t->event, &t->frame_timer, CLOCK_MONOTONIC, 0, 0, terminal_frame_timer_fn, t);
if (r < 0) {
- log_error("error: sd_event_add_time() (%d): %s", r, strerror(-r));
+ log_error_errno(r, "error: sd_event_add_time() (%d): %m", r);
goto error;
}
r = term_screen_resize(t->screen, t->output->in_width, t->output->in_height);
if (r < 0) {
- log_error("error: term_screen_resize() (%d): %s", r, strerror(-r));
+ log_error_errno(r, "error: term_screen_resize() (%d): %m", r);
goto error;
}
assert_return(t, -EINVAL);
pid = pty_fork(&t->pty, t->event, terminal_pty_fn, t, t->output->in_width, t->output->in_height);
- if (pid < 0) {
- log_error("error: cannot fork PTY (%d): %s", pid, strerror(-pid));
- return pid;
- } else if (pid == 0) {
+ if (pid < 0)
+ return log_error_errno(pid, "error: cannot fork PTY (%d): %m", pid);
+ else if (pid == 0) {
/* child */
char **argv = (char*[]){
setenv("COLORTERM", "systemd-subterm", 1);
execve(argv[0], argv, environ);
- log_error("error: cannot exec %s (%d): %m", argv[0], -errno);
+ log_error_errno(errno, "error: cannot exec %s (%d): %m", argv[0], -errno);
_exit(1);
}
out:
if (r < 0)
- log_error("error: terminal failed (%d): %s", r, strerror(-r));
+ log_error_errno(r, "error: terminal failed (%d): %m", r);
terminal_free(t);
return -r;
}