chiark / gitweb /
shared: utf8 - support ucs4 -> utf8
[elogind.git] / src / libsystemd-terminal / subterm.c
index 72ce2f6f6e6bfdb83f9d45a34217b1566dd44bf7..7c119ac58aa02eb4e96953bde9c48d5be66af40a 100644 (file)
@@ -41,6 +41,7 @@
 #include "sd-event.h"
 #include "term-internal.h"
 #include "util.h"
+#include "utf8.h"
 
 typedef struct Output Output;
 typedef struct Terminal Terminal;
@@ -102,10 +103,8 @@ static int output_winch(Output *o) {
         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;
@@ -119,16 +118,14 @@ static int output_winch(Output *o) {
 }
 
 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;
 
@@ -156,14 +153,13 @@ static int output_write(Output *o, const void *buf, size_t size) {
                 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];
         int r;
@@ -179,6 +175,7 @@ static int output_vnprintf(Output *o, size_t max, const char *format, va_list ar
         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;
@@ -190,6 +187,7 @@ static int output_nprintf(Output *o, size_t max, const char *format, ...) {
         return r;
 }
 
+_printf_(2,0)
 static int output_vprintf(Output *o, const char *format, va_list args) {
         char buf[4096];
         int r;
@@ -204,6 +202,7 @@ static int output_vprintf(Output *o, const char *format, va_list args) {
         return output_write(o, buf, r);
 }
 
+_printf_(2,3)
 static int output_printf(Output *o, const char *format, ...) {
         va_list args;
         int r;
@@ -256,6 +255,7 @@ static int output_print_line(Output *o, size_t len) {
         return r;
 }
 
+_printf_(2,3)
 static int output_frame_printl(Output *o, const char *format, ...) {
         va_list args;
         int r;
@@ -281,6 +281,8 @@ static Output *output_free(Output *o) {
         if (!o)
                 return NULL;
 
+        /* re-enable cursor */
+        output_printf(o, "\e[?25h");
         /* disable alternate screen buffer */
         output_printf(o, "\e[?1049l");
         output_flush(o);
@@ -312,6 +314,11 @@ static int output_new(Output **out, int fd) {
         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;
@@ -381,92 +388,92 @@ static void output_draw_frame(Output *o) {
 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");
 }
 
@@ -530,10 +537,6 @@ static void output_draw(Output *o, bool menu, term_screen *screen) {
         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
@@ -559,7 +562,7 @@ static void output_draw(Output *o, bool menu, term_screen *screen) {
  */
 
 static void terminal_dirty(Terminal *t) {
-        uint64_t usec;
+        usec_t usec;
         int r;
 
         assert(t);
@@ -604,12 +607,12 @@ static int terminal_winch_fn(sd_event_source *source, const struct signalfd_sigi
         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);
 
@@ -623,7 +626,7 @@ static int terminal_push_tmp(Terminal *t, uint32_t ucs4) {
 
         assert(t);
 
-        len = term_utf8_encode(buf, ucs4);
+        len = utf8_encode_unichar(buf, ucs4);
         if (len < 1)
                 return 0;
 
@@ -648,10 +651,8 @@ static int terminal_write_tmp(Terminal *t) {
         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);
                 }
         }
 
@@ -705,22 +706,20 @@ static int terminal_io_fn(sd_event_source *source, int fd, uint32_t revents, voi
                 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]);
@@ -769,10 +768,8 @@ static int terminal_pty_fn(Pty *pty, void *userdata, unsigned int event, const v
                 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;
@@ -824,16 +821,12 @@ static int terminal_new(Terminal **out, int in_fd, int out_fd) {
         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)
@@ -849,49 +842,49 @@ static int terminal_new(Terminal **out, int in_fd, int out_fd) {
 
         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;
         }
 
@@ -899,7 +892,7 @@ static int terminal_new(Terminal **out, int in_fd, int out_fd) {
         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;
         }
 
@@ -921,7 +914,7 @@ static int terminal_new(Terminal **out, int in_fd, int out_fd) {
 
         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;
         }
 
@@ -943,10 +936,9 @@ static int terminal_run(Terminal *t) {
         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*[]){
@@ -958,7 +950,7 @@ static int terminal_run(Terminal *t) {
                 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);
         }
 
@@ -985,7 +977,7 @@ int main(int argc, char *argv[]) {
 
 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;
 }