Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/app/terminal/terminal.c

    r01cd15bc r266ec54  
    11/*
    2  * Copyright (c) 2025 Jiri Svoboda
     2 * Copyright (c) 2020 Jiri Svoboda
    33 * Copyright (c) 2012 Petr Koupy
    44 * All rights reserved.
     
    3737#include <adt/list.h>
    3838#include <adt/prodcons.h>
    39 #include <as.h>
    4039#include <errno.h>
    4140#include <fbfont/font-8x16.h>
    42 #include <fibril.h>
     41#include <io/chargrid.h>
    4342#include <gfx/bitmap.h>
    4443#include <gfx/context.h>
    45 #include <gfx/render.h>
    4644#include <io/con_srv.h>
    4745#include <io/concaps.h>
    4846#include <io/console.h>
    4947#include <io/pixelmap.h>
    50 #include <macros.h>
     48#include <task.h>
    5149#include <stdarg.h>
    52 #include <stdio.h>
    5350#include <stdlib.h>
    54 #include <str_error.h>
    5551#include <str.h>
    56 #include <task.h>
    5752#include <ui/resource.h>
    5853#include <ui/ui.h>
     
    7065
    7166#define TERM_CAPS \
    72         (CONSOLE_CAP_CURSORCTL | CONSOLE_CAP_STYLE | CONSOLE_CAP_INDEXED | \
    73         CONSOLE_CAP_RGB)
    74 
    75 #define SCROLLBACK_MAX_LINES 1000
    76 #define MIN_WINDOW_COLS 8
    77 #define MIN_WINDOW_ROWS 4
     67        (CONSOLE_CAP_STYLE | CONSOLE_CAP_INDEXED | CONSOLE_CAP_RGB)
    7868
    7969static LIST_INITIALIZE(terms);
    80 
    81 #define COLOR_BRIGHT 8
    82 
    83 static const pixel_t _basic_colors[16] = {
    84         [COLOR_BLACK]       = PIXEL(255, 0, 0, 0),
    85         [COLOR_RED]         = PIXEL(255, 170, 0, 0),
    86         [COLOR_GREEN]       = PIXEL(255, 0, 170, 0),
    87         [COLOR_YELLOW]      = PIXEL(255, 170, 85, 0),
    88         [COLOR_BLUE]        = PIXEL(255, 0, 0, 170),
    89         [COLOR_MAGENTA]     = PIXEL(255, 170, 0, 170),
    90         [COLOR_CYAN]        = PIXEL(255, 0, 170, 170),
    91         [COLOR_WHITE]       = PIXEL(255, 170, 170, 170),
    92 
    93         [COLOR_BLACK   | COLOR_BRIGHT] = PIXEL(255, 85, 85, 85),
    94         [COLOR_RED     | COLOR_BRIGHT] = PIXEL(255, 255, 85, 85),
    95         [COLOR_GREEN   | COLOR_BRIGHT] = PIXEL(255, 85, 255, 85),
    96         [COLOR_YELLOW  | COLOR_BRIGHT] = PIXEL(255, 255, 255, 85),
    97         [COLOR_BLUE    | COLOR_BRIGHT] = PIXEL(255, 85, 85, 255),
    98         [COLOR_MAGENTA | COLOR_BRIGHT] = PIXEL(255, 255, 85, 255),
    99         [COLOR_CYAN    | COLOR_BRIGHT] = PIXEL(255, 85, 255, 255),
    100         [COLOR_WHITE   | COLOR_BRIGHT] = PIXEL(255, 255, 255, 255),
    101 };
    10270
    10371static errno_t term_open(con_srvs_t *, con_srv_t *);
     
    11684static void term_set_rgb_color(con_srv_t *, pixel_t, pixel_t);
    11785static void term_set_cursor_visibility(con_srv_t *, bool);
    118 static errno_t term_set_caption(con_srv_t *, const char *);
    11986static errno_t term_get_event(con_srv_t *, cons_event_t *);
    120 static errno_t term_map(con_srv_t *, sysarg_t, sysarg_t, charfield_t **);
    121 static void term_unmap(con_srv_t *);
    122 static void term_buf_update(con_srv_t *, sysarg_t, sysarg_t, sysarg_t,
    123     sysarg_t);
    12487
    12588static con_ops_t con_ops = {
     
    138101        .set_rgb_color = term_set_rgb_color,
    139102        .set_cursor_visibility = term_set_cursor_visibility,
    140         .set_caption = term_set_caption,
    141         .get_event = term_get_event,
    142         .map = term_map,
    143         .unmap = term_unmap,
    144         .update = term_buf_update
     103        .get_event = term_get_event
    145104};
    146105
    147106static void terminal_close_event(ui_window_t *, void *);
    148 static void terminal_focus_event(ui_window_t *, void *, unsigned);
    149 static void terminal_resize_event(ui_window_t *, void *);
     107static void terminal_focus_event(ui_window_t *, void *);
    150108static void terminal_kbd_event(ui_window_t *, void *, kbd_event_t *);
    151109static void terminal_pos_event(ui_window_t *, void *, pos_event_t *);
    152 static void terminal_unfocus_event(ui_window_t *, void *, unsigned);
    153 static void terminal_maximize_event(ui_window_t *, void *);
    154 static void terminal_unmaximize_event(ui_window_t *, void *);
     110static void terminal_unfocus_event(ui_window_t *, void *);
    155111
    156112static ui_window_cb_t terminal_window_cb = {
    157113        .close = terminal_close_event,
    158114        .focus = terminal_focus_event,
    159         .resize = terminal_resize_event,
    160115        .kbd = terminal_kbd_event,
    161116        .pos = terminal_pos_event,
    162         .unfocus = terminal_unfocus_event,
    163         .maximize = terminal_maximize_event,
    164         .unmaximize = terminal_unmaximize_event,
     117        .unfocus = terminal_unfocus_event
    165118};
    166119
    167 static errno_t terminal_wait_fibril(void *);
    168 
    169120static terminal_t *srv_to_terminal(con_srv_t *srv)
    170121{
     
    172123}
    173124
    174 static errno_t getterm(task_wait_t *wait, const char *svc, const char *app)
    175 {
    176         return task_spawnl(NULL, wait, APP_GETTERM, APP_GETTERM, svc,
     125static void getterm(const char *svc, const char *app)
     126{
     127        task_spawnl(NULL, NULL, APP_GETTERM, APP_GETTERM, svc,
    177128            LOCFS_MOUNT_POINT, "--msg", "--wait", "--", app, NULL);
    178129}
    179130
    180 static pixel_t termui_color_to_pixel(termui_color_t c)
    181 {
    182         uint8_t r, g, b;
    183         termui_color_to_rgb(c, &r, &g, &b);
    184         return PIXEL(255, r, g, b);
    185 }
    186 
    187 static termui_color_t termui_color_from_pixel(pixel_t pixel)
    188 {
    189         return termui_color_from_rgb(RED(pixel), GREEN(pixel), BLUE(pixel));
    190 }
    191 
    192 static termui_cell_t charfield_to_termui_cell(terminal_t *term, const charfield_t *cf)
    193 {
    194         termui_cell_t cell = { };
    195 
    196         cell.glyph_idx = fb_font_glyph(cf->ch, NULL);
    197 
    198         switch (cf->attrs.type) {
     131static pixel_t color_table[16] = {
     132        [COLOR_BLACK]       = PIXEL(255, 0, 0, 0),
     133        [COLOR_BLUE]        = PIXEL(255, 0, 0, 240),
     134        [COLOR_GREEN]       = PIXEL(255, 0, 240, 0),
     135        [COLOR_CYAN]        = PIXEL(255, 0, 240, 240),
     136        [COLOR_RED]         = PIXEL(255, 240, 0, 0),
     137        [COLOR_MAGENTA]     = PIXEL(255, 240, 0, 240),
     138        [COLOR_YELLOW]      = PIXEL(255, 240, 240, 0),
     139        [COLOR_WHITE]       = PIXEL(255, 240, 240, 240),
     140
     141        [COLOR_BLACK + 8]   = PIXEL(255, 0, 0, 0),
     142        [COLOR_BLUE + 8]    = PIXEL(255, 0, 0, 255),
     143        [COLOR_GREEN + 8]   = PIXEL(255, 0, 255, 0),
     144        [COLOR_CYAN + 8]    = PIXEL(255, 0, 255, 255),
     145        [COLOR_RED + 8]     = PIXEL(255, 255, 0, 0),
     146        [COLOR_MAGENTA + 8] = PIXEL(255, 255, 0, 255),
     147        [COLOR_YELLOW + 8]  = PIXEL(255, 255, 255, 0),
     148        [COLOR_WHITE + 8]   = PIXEL(255, 255, 255, 255),
     149};
     150
     151static inline void attrs_rgb(char_attrs_t attrs, pixel_t *bgcolor, pixel_t *fgcolor)
     152{
     153        switch (attrs.type) {
    199154        case CHAR_ATTR_STYLE:
    200                 switch (cf->attrs.val.style) {
     155                switch (attrs.val.style) {
    201156                case STYLE_NORMAL:
    202                         cell.bgcolor = term->default_bgcolor;
    203                         cell.fgcolor = term->default_fgcolor;
     157                        *bgcolor = color_table[COLOR_WHITE];
     158                        *fgcolor = color_table[COLOR_BLACK];
    204159                        break;
    205160                case STYLE_EMPHASIS:
    206                         cell.bgcolor = term->emphasis_bgcolor;
    207                         cell.fgcolor = term->emphasis_fgcolor;
     161                        *bgcolor = color_table[COLOR_WHITE];
     162                        *fgcolor = color_table[COLOR_RED];
    208163                        break;
    209164                case STYLE_INVERTED:
    210                         cell.bgcolor = term->default_bgcolor;
    211                         cell.fgcolor = term->default_fgcolor;
    212                         cell.inverted = 1;
     165                        *bgcolor = color_table[COLOR_BLACK];
     166                        *fgcolor = color_table[COLOR_WHITE];
    213167                        break;
    214168                case STYLE_SELECTED:
    215                         cell.bgcolor = term->selection_bgcolor;
    216                         cell.fgcolor = term->selection_fgcolor;
     169                        *bgcolor = color_table[COLOR_RED];
     170                        *fgcolor = color_table[COLOR_WHITE];
    217171                        break;
    218172                }
    219173                break;
    220 
    221174        case CHAR_ATTR_INDEX:
    222                 char_attr_index_t index = cf->attrs.val.index;
    223 
    224                 int bright = (index.attr & CATTR_BRIGHT) ? COLOR_BRIGHT : 0;
    225                 pixel_t bgcolor = _basic_colors[index.bgcolor];
    226                 pixel_t fgcolor = _basic_colors[index.fgcolor | bright];
    227                 cell.bgcolor = termui_color_from_pixel(bgcolor);
    228                 cell.fgcolor = termui_color_from_pixel(fgcolor);
    229 
    230                 if (index.attr & CATTR_BLINK)
    231                         cell.blink = 1;
    232 
     175                *bgcolor = color_table[(attrs.val.index.bgcolor & 7) |
     176                    ((attrs.val.index.attr & CATTR_BRIGHT) ? 8 : 0)];
     177                *fgcolor = color_table[(attrs.val.index.fgcolor & 7) |
     178                    ((attrs.val.index.attr & CATTR_BRIGHT) ? 8 : 0)];
    233179                break;
    234 
    235180        case CHAR_ATTR_RGB:
    236                 cell.bgcolor = termui_color_from_pixel(cf->attrs.val.rgb.bgcolor);
    237                 cell.fgcolor = termui_color_from_pixel(cf->attrs.val.rgb.fgcolor);
     181                *bgcolor = 0xff000000 | attrs.val.rgb.bgcolor;
     182                *fgcolor = 0xff000000 | attrs.val.rgb.fgcolor;
    238183                break;
    239184        }
    240 
    241         return cell;
    242185}
    243186
     
    257200}
    258201
    259 static void term_draw_cell(terminal_t *term, pixelmap_t *pixelmap, int col, int row, const termui_cell_t *cell)
    260 {
    261         termui_color_t bg = cell->bgcolor;
    262         if (bg == TERMUI_COLOR_DEFAULT)
    263                 bg = term->default_bgcolor;
    264 
    265         termui_color_t fg = cell->fgcolor;
    266         if (fg == TERMUI_COLOR_DEFAULT)
    267                 fg = term->default_fgcolor;
    268 
    269         pixel_t bgcolor = termui_color_to_pixel(bg);
    270         pixel_t fgcolor = termui_color_to_pixel(fg);
    271 
    272         int bx = col * FONT_WIDTH;
    273         int by = row * FONT_SCANLINES;
    274 
    275         // TODO: support bold/italic/underline/strike/blink styling
    276 
    277         if (cell->inverted ^ cell->cursor) {
    278                 pixel_t tmp = bgcolor;
    279                 bgcolor = fgcolor;
    280                 fgcolor = tmp;
    281         }
    282 
    283         uint32_t glyph = cell->glyph_idx;
    284         assert(glyph < FONT_GLYPHS);
    285 
    286         if (glyph == 0)
    287                 glyph = fb_font_glyph(U' ', NULL);
     202static void term_update_char(terminal_t *term, pixelmap_t *pixelmap,
     203    sysarg_t sx, sysarg_t sy, sysarg_t col, sysarg_t row)
     204{
     205        charfield_t *field =
     206            chargrid_charfield_at(term->backbuf, col, row);
     207
     208        bool inverted = chargrid_cursor_at(term->backbuf, col, row);
     209
     210        sysarg_t bx = sx + (col * FONT_WIDTH);
     211        sysarg_t by = sy + (row * FONT_SCANLINES);
     212
     213        pixel_t bgcolor = 0;
     214        pixel_t fgcolor = 0;
     215
     216        if (inverted)
     217                attrs_rgb(field->attrs, &fgcolor, &bgcolor);
     218        else
     219                attrs_rgb(field->attrs, &bgcolor, &fgcolor);
     220
     221        // FIXME: Glyph type should be actually uint32_t
     222        //        for full UTF-32 coverage.
     223
     224        uint16_t glyph = fb_font_glyph(field->ch, NULL);
    288225
    289226        for (unsigned int y = 0; y < FONT_SCANLINES; y++) {
     
    297234                }
    298235        }
    299 
    300236        term_update_region(term, bx, by, FONT_WIDTH, FONT_SCANLINES);
    301237}
    302238
    303 static void term_render(terminal_t *term)
    304 {
    305         (void) gfx_bitmap_render(term->bmp, &term->update, &term->off);
    306 
    307         term->update.p0.x = 0;
    308         term->update.p0.y = 0;
    309         term->update.p1.x = 0;
    310         term->update.p1.y = 0;
    311 }
    312 
    313 static void termui_refresh_cb(void *userdata)
    314 {
    315         terminal_t *term = userdata;
    316 
    317         termui_force_viewport_update(term->termui, 0, termui_get_rows(term->termui));
    318 }
    319 
    320 static void termui_scroll_cb(void *userdata, int delta)
    321 {
    322         (void) delta;
    323 
    324         // Until we have support for hardware accelerated scrolling, just redraw everything.
    325         termui_refresh_cb(userdata);
    326 }
    327 
    328 static pixelmap_t term_get_pixelmap(terminal_t *term)
    329 {
    330         pixelmap_t pixelmap = { };
     239static bool term_update_scroll(terminal_t *term, pixelmap_t *pixelmap,
     240    sysarg_t sx, sysarg_t sy)
     241{
     242        sysarg_t top_row = chargrid_get_top_row(term->frontbuf);
     243
     244        if (term->top_row == top_row) {
     245                return false;
     246        }
     247
     248        term->top_row = top_row;
     249
     250        for (sysarg_t row = 0; row < term->rows; row++) {
     251                for (sysarg_t col = 0; col < term->cols; col++) {
     252                        charfield_t *front_field =
     253                            chargrid_charfield_at(term->frontbuf, col, row);
     254                        charfield_t *back_field =
     255                            chargrid_charfield_at(term->backbuf, col, row);
     256                        bool update = false;
     257
     258                        if (front_field->ch != back_field->ch) {
     259                                back_field->ch = front_field->ch;
     260                                update = true;
     261                        }
     262
     263                        if (!attrs_same(front_field->attrs, back_field->attrs)) {
     264                                back_field->attrs = front_field->attrs;
     265                                update = true;
     266                        }
     267
     268                        front_field->flags &= ~CHAR_FLAG_DIRTY;
     269
     270                        if (update) {
     271                                term_update_char(term, pixelmap, sx, sy, col, row);
     272                        }
     273                }
     274        }
     275
     276        return true;
     277}
     278
     279static bool term_update_cursor(terminal_t *term, pixelmap_t *pixelmap,
     280    sysarg_t sx, sysarg_t sy)
     281{
     282        bool update = false;
     283
     284        sysarg_t front_col;
     285        sysarg_t front_row;
     286        chargrid_get_cursor(term->frontbuf, &front_col, &front_row);
     287
     288        sysarg_t back_col;
     289        sysarg_t back_row;
     290        chargrid_get_cursor(term->backbuf, &back_col, &back_row);
     291
     292        bool front_visibility =
     293            chargrid_get_cursor_visibility(term->frontbuf) &&
     294            term->is_focused;
     295        bool back_visibility =
     296            chargrid_get_cursor_visibility(term->backbuf);
     297
     298        if (front_visibility != back_visibility) {
     299                chargrid_set_cursor_visibility(term->backbuf,
     300                    front_visibility);
     301                term_update_char(term, pixelmap, sx, sy, back_col, back_row);
     302                update = true;
     303        }
     304
     305        if ((front_col != back_col) || (front_row != back_row)) {
     306                chargrid_set_cursor(term->backbuf, front_col, front_row);
     307                term_update_char(term, pixelmap, sx, sy, back_col, back_row);
     308                term_update_char(term, pixelmap, sx, sy, front_col, front_row);
     309                update = true;
     310        }
     311
     312        return update;
     313}
     314
     315static void term_update(terminal_t *term)
     316{
     317        pixelmap_t pixelmap;
    331318        gfx_bitmap_alloc_t alloc;
    332 
    333         errno_t rc = gfx_bitmap_get_alloc(term->bmp, &alloc);
    334         if (rc != EOK)
    335                 return pixelmap;
    336 
     319        gfx_coord2_t pos;
     320        errno_t rc;
     321
     322        rc = gfx_bitmap_get_alloc(term->bmp, &alloc);
     323        if (rc != EOK) {
     324                return;
     325        }
     326
     327        fibril_mutex_lock(&term->mtx);
    337328        pixelmap.width = term->w;
    338329        pixelmap.height = term->h;
    339330        pixelmap.data = alloc.pixels;
    340         return pixelmap;
    341 }
    342 
    343 static void term_clear_bitmap(terminal_t *term, pixel_t color)
    344 {
    345         pixelmap_t pixelmap = term_get_pixelmap(term);
    346         if (pixelmap.data == NULL)
     331
     332        bool update = false;
     333        sysarg_t sx = 0/*term->widget.hpos*/;
     334        sysarg_t sy = 0/*term->widget.vpos*/;
     335
     336        if (term_update_scroll(term, &pixelmap, sx, sy)) {
     337                update = true;
     338        } else {
     339                for (sysarg_t y = 0; y < term->rows; y++) {
     340                        for (sysarg_t x = 0; x < term->cols; x++) {
     341                                charfield_t *front_field =
     342                                    chargrid_charfield_at(term->frontbuf, x, y);
     343                                charfield_t *back_field =
     344                                    chargrid_charfield_at(term->backbuf, x, y);
     345                                bool update = false;
     346
     347                                if ((front_field->flags & CHAR_FLAG_DIRTY) ==
     348                                    CHAR_FLAG_DIRTY) {
     349                                        if (front_field->ch != back_field->ch) {
     350                                                back_field->ch = front_field->ch;
     351                                                update = true;
     352                                        }
     353
     354                                        if (!attrs_same(front_field->attrs,
     355                                            back_field->attrs)) {
     356                                                back_field->attrs = front_field->attrs;
     357                                                update = true;
     358                                        }
     359
     360                                        front_field->flags &= ~CHAR_FLAG_DIRTY;
     361                                }
     362
     363                                if (update) {
     364                                        term_update_char(term, &pixelmap, sx, sy, x, y);
     365                                        update = true;
     366                                }
     367                        }
     368                }
     369        }
     370
     371        if (term_update_cursor(term, &pixelmap, sx, sy))
     372                update = true;
     373
     374        if (update) {
     375                pos.x = 4;
     376                pos.y = 26;
     377                (void) gfx_bitmap_render(term->bmp, &term->update, &pos);
     378
     379                term->update.p0.x = 0;
     380                term->update.p0.y = 0;
     381                term->update.p1.x = 0;
     382                term->update.p1.y = 0;
     383        }
     384
     385        fibril_mutex_unlock(&term->mtx);
     386}
     387
     388static void term_repaint(terminal_t *term)
     389{
     390        pixelmap_t pixelmap;
     391        gfx_bitmap_alloc_t alloc;
     392        errno_t rc;
     393
     394        rc = gfx_bitmap_get_alloc(term->bmp, &alloc);
     395        if (rc != EOK) {
     396                printf("Error getting bitmap allocation info.\n");
    347397                return;
    348 
    349         sysarg_t pixels = pixelmap.height * pixelmap.width;
    350         for (sysarg_t i = 0; i < pixels; i++)
    351                 pixelmap.data[i] = color;
    352 
    353         term_update_region(term, 0, 0, pixelmap.width, pixelmap.height);
    354 }
    355 
    356 static void termui_update_cb(void *userdata, int col, int row, const termui_cell_t *cell, int len)
    357 {
    358         terminal_t *term = userdata;
    359 
    360         pixelmap_t pixelmap = term_get_pixelmap(term);
    361         if (pixelmap.data == NULL)
    362                 return;
    363 
    364         for (int i = 0; i < len; i++)
    365                 term_draw_cell(term, &pixelmap, col + i, row, &cell[i]);
     398        }
     399
     400        fibril_mutex_lock(&term->mtx);
     401
     402        pixelmap.width = term->w;
     403        pixelmap.height = term->h;
     404        pixelmap.data = alloc.pixels;
     405
     406        sysarg_t sx = 0;
     407        sysarg_t sy = 0;
     408
     409        if (!term_update_scroll(term, &pixelmap, sx, sy)) {
     410                for (sysarg_t y = 0; y < term->rows; y++) {
     411                        for (sysarg_t x = 0; x < term->cols; x++) {
     412                                charfield_t *front_field =
     413                                    chargrid_charfield_at(term->frontbuf, x, y);
     414                                charfield_t *back_field =
     415                                    chargrid_charfield_at(term->backbuf, x, y);
     416
     417                                back_field->ch = front_field->ch;
     418                                back_field->attrs = front_field->attrs;
     419                                front_field->flags &= ~CHAR_FLAG_DIRTY;
     420
     421                                term_update_char(term, &pixelmap, sx, sy, x, y);
     422                        }
     423                }
     424        }
     425
     426        term_update_cursor(term, &pixelmap, sx, sy);
     427
     428        fibril_mutex_unlock(&term->mtx);
    366429}
    367430
     
    403466                if (pos < size) {
    404467                        link_t *link = prodcons_consume(&term->input_pc);
    405                         terminal_event_t *qevent = list_get_instance(link,
    406                             terminal_event_t, link);
    407                         cons_event_t *event = &qevent->ev;
     468                        cons_event_t *event = list_get_instance(link, cons_event_t, link);
    408469
    409470                        /* Accept key presses of printable chars only. */
     
    419480                        }
    420481
    421                         free(qevent);
     482                        free(event);
    422483                }
    423484        }
     
    429490static void term_write_char(terminal_t *term, wchar_t ch)
    430491{
     492        sysarg_t updated = 0;
     493
     494        fibril_mutex_lock(&term->mtx);
     495
    431496        switch (ch) {
    432         case L'\n':
    433                 termui_put_crlf(term->termui);
     497        case '\n':
     498                updated = chargrid_newline(term->frontbuf);
    434499                break;
    435         case L'\r':
    436                 termui_put_cr(term->termui);
     500        case '\r':
    437501                break;
    438         case L'\t':
    439                 termui_put_tab(term->termui);
     502        case '\t':
     503                updated = chargrid_tabstop(term->frontbuf, 8);
    440504                break;
    441         case L'\b':
    442                 termui_put_backspace(term->termui);
     505        case '\b':
     506                updated = chargrid_backspace(term->frontbuf);
    443507                break;
    444508        default:
    445                 // TODO: For some languages, we might need support for combining
    446                 //       characters. Currently, we assume every unicode code point is
    447                 //       an individual printed character, which is not always the case.
    448                 termui_put_glyph(term->termui, fb_font_glyph(ch, NULL), 1);
    449                 break;
    450         }
     509                updated = chargrid_putuchar(term->frontbuf, ch, true);
     510        }
     511
     512        fibril_mutex_unlock(&term->mtx);
     513
     514        if (updated > 1)
     515                term_update(term);
    451516}
    452517
     
    454519{
    455520        terminal_t *term = srv_to_terminal(srv);
    456 
    457         fibril_mutex_lock(&term->mtx);
    458521
    459522        size_t off = 0;
     
    461524                term_write_char(term, str_decode(data, &off, size));
    462525
    463         fibril_mutex_unlock(&term->mtx);
    464 
    465         term_render(term);
    466         gfx_update(term->gc);
    467526        *nwritten = size;
    468 
    469527        return EOK;
    470528}
     
    474532        terminal_t *term = srv_to_terminal(srv);
    475533
    476         term_render(term);
    477         gfx_update(term->gc);
     534        term_update(term);
    478535}
    479536
     
    483540
    484541        fibril_mutex_lock(&term->mtx);
    485         termui_clear_screen(term->termui);
    486         fibril_mutex_unlock(&term->mtx);
    487 
    488         term_render(term);
    489         gfx_update(term->gc);
     542        chargrid_clear(term->frontbuf);
     543        fibril_mutex_unlock(&term->mtx);
     544
     545        term_update(term);
    490546}
    491547
     
    495551
    496552        fibril_mutex_lock(&term->mtx);
    497         termui_set_pos(term->termui, col, row);
    498         fibril_mutex_unlock(&term->mtx);
    499 
    500         term_render(term);
    501         gfx_update(term->gc);
     553        chargrid_set_cursor(term->frontbuf, col, row);
     554        fibril_mutex_unlock(&term->mtx);
     555
     556        term_update(term);
    502557}
    503558
     
    507562
    508563        fibril_mutex_lock(&term->mtx);
    509         int irow, icol;
    510         termui_get_pos(term->termui, &icol, &irow);
    511         fibril_mutex_unlock(&term->mtx);
    512 
    513         *col = icol;
    514         *row = irow;
     564        chargrid_get_cursor(term->frontbuf, col, row);
     565        fibril_mutex_unlock(&term->mtx);
    515566
    516567        return EOK;
     
    522573
    523574        fibril_mutex_lock(&term->mtx);
    524         *cols = termui_get_cols(term->termui);
    525         *rows = termui_get_rows(term->termui);
     575        *cols = term->cols;
     576        *rows = term->rows;
    526577        fibril_mutex_unlock(&term->mtx);
    527578
     
    541592        terminal_t *term = srv_to_terminal(srv);
    542593
    543         termui_cell_t cellstyle = { };
    544 
    545         switch (style) {
    546         case STYLE_NORMAL:
    547                 cellstyle.bgcolor = term->default_bgcolor;
    548                 cellstyle.fgcolor = term->default_fgcolor;
    549                 break;
    550         case STYLE_EMPHASIS:
    551                 cellstyle.bgcolor = term->emphasis_bgcolor;
    552                 cellstyle.fgcolor = term->emphasis_fgcolor;
    553                 break;
    554         case STYLE_INVERTED:
    555                 cellstyle.bgcolor = term->default_bgcolor;
    556                 cellstyle.fgcolor = term->default_fgcolor;
    557                 cellstyle.inverted = 1;
    558                 break;
    559         case STYLE_SELECTED:
    560                 cellstyle.bgcolor = term->selection_bgcolor;
    561                 cellstyle.fgcolor = term->selection_fgcolor;
    562                 break;
    563         }
    564 
    565         fibril_mutex_lock(&term->mtx);
    566         termui_set_style(term->termui, cellstyle);
     594        fibril_mutex_lock(&term->mtx);
     595        chargrid_set_style(term->frontbuf, style);
    567596        fibril_mutex_unlock(&term->mtx);
    568597}
     
    573602        terminal_t *term = srv_to_terminal(srv);
    574603
    575         int bright = (attr & CATTR_BRIGHT) ? COLOR_BRIGHT : 0;
    576 
    577         termui_cell_t cellstyle = { };
    578         cellstyle.bgcolor = termui_color_from_pixel(_basic_colors[bgcolor]);
    579         cellstyle.fgcolor = termui_color_from_pixel(_basic_colors[fgcolor | bright]);
    580 
    581         if (attr & CATTR_BLINK)
    582                 cellstyle.blink = 1;
    583 
    584         fibril_mutex_lock(&term->mtx);
    585         termui_set_style(term->termui, cellstyle);
     604        fibril_mutex_lock(&term->mtx);
     605        chargrid_set_color(term->frontbuf, bgcolor, fgcolor, attr);
    586606        fibril_mutex_unlock(&term->mtx);
    587607}
     
    591611{
    592612        terminal_t *term = srv_to_terminal(srv);
    593         termui_cell_t cellstyle = {
    594                 .bgcolor = termui_color_from_pixel(bgcolor),
    595                 .fgcolor = termui_color_from_pixel(fgcolor),
    596         };
    597 
    598         fibril_mutex_lock(&term->mtx);
    599         termui_set_style(term->termui, cellstyle);
     613
     614        fibril_mutex_lock(&term->mtx);
     615        chargrid_set_rgb_color(term->frontbuf, bgcolor, fgcolor);
    600616        fibril_mutex_unlock(&term->mtx);
    601617}
     
    606622
    607623        fibril_mutex_lock(&term->mtx);
    608         termui_set_cursor_visibility(term->termui, visible);
    609         fibril_mutex_unlock(&term->mtx);
    610 
    611         term_render(term);
    612         gfx_update(term->gc);
    613 }
    614 
    615 static errno_t term_set_caption(con_srv_t *srv, const char *caption)
    616 {
    617         terminal_t *term = srv_to_terminal(srv);
    618         const char *cap;
    619 
    620         fibril_mutex_lock(&term->mtx);
    621 
    622         if (str_size(caption) > 0)
    623                 cap = caption;
    624         else
    625                 cap = "Terminal";
    626 
    627         ui_window_set_caption(term->window, cap);
    628         fibril_mutex_unlock(&term->mtx);
    629 
    630         term_render(term);
    631         gfx_update(term->gc);
    632         return EOK;
     624        chargrid_set_cursor_visibility(term->frontbuf, visible);
     625        fibril_mutex_unlock(&term->mtx);
     626
     627        term_update(term);
    633628}
    634629
     
    637632        terminal_t *term = srv_to_terminal(srv);
    638633        link_t *link = prodcons_consume(&term->input_pc);
    639         terminal_event_t *ev = list_get_instance(link, terminal_event_t, link);
    640 
    641         *event = ev->ev;
     634        cons_event_t *ev = list_get_instance(link, cons_event_t, link);
     635
     636        *event = *ev;
    642637        free(ev);
    643638        return EOK;
    644639}
    645640
    646 /** Create shared buffer for efficient rendering.
    647  *
    648  * @param srv Console server
    649  * @param cols Number of columns in buffer
    650  * @param rows Number of rows in buffer
    651  * @param rbuf Place to store pointer to new sharable buffer
    652  *
    653  * @return EOK on sucess or an error code
    654  */
    655 static errno_t term_map(con_srv_t *srv, sysarg_t cols, sysarg_t rows,
    656     charfield_t **rbuf)
    657 {
    658         terminal_t *term = srv_to_terminal(srv);
    659         void *buf;
    660 
    661         fibril_mutex_lock(&term->mtx);
    662 
    663         if (term->ubuf != NULL) {
    664                 fibril_mutex_unlock(&term->mtx);
    665                 return EBUSY;
    666         }
    667 
    668         buf = as_area_create(AS_AREA_ANY, cols * rows * sizeof(charfield_t),
    669             AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE, AS_AREA_UNPAGED);
    670         if (buf == AS_MAP_FAILED) {
    671                 fibril_mutex_unlock(&term->mtx);
     641static void deinit_terminal(terminal_t *term)
     642{
     643        list_remove(&term->link);
     644
     645        if (term->frontbuf)
     646                chargrid_destroy(term->frontbuf);
     647
     648        if (term->backbuf)
     649                chargrid_destroy(term->backbuf);
     650}
     651
     652void terminal_destroy(terminal_t *term)
     653{
     654        deinit_terminal(term);
     655        free(term);
     656}
     657
     658static void terminal_queue_cons_event(terminal_t *term, cons_event_t *ev)
     659{
     660        /* Got key press/release event */
     661        cons_event_t *event =
     662            (cons_event_t *) malloc(sizeof(cons_event_t));
     663        if (event == NULL)
     664                return;
     665
     666        *event = *ev;
     667        link_initialize(&event->link);
     668
     669        prodcons_produce(&term->input_pc, &event->link);
     670}
     671
     672/** Handle window close event. */
     673static void terminal_close_event(ui_window_t *window, void *arg)
     674{
     675        terminal_t *term = (terminal_t *) arg;
     676
     677        (void) term;
     678
     679        // XXX This is not really a clean way of terminating
     680        exit(0);
     681}
     682
     683/** Handle window focus event. */
     684static void terminal_focus_event(ui_window_t *window, void *arg)
     685{
     686        terminal_t *term = (terminal_t *) arg;
     687
     688        term->is_focused = true;
     689        term_update(term);
     690}
     691
     692/** Handle window keyboard event */
     693static void terminal_kbd_event(ui_window_t *window, void *arg,
     694    kbd_event_t *kbd_event)
     695{
     696        terminal_t *term = (terminal_t *) arg;
     697        cons_event_t event;
     698
     699        event.type = CEV_KEY;
     700        event.ev.key = *kbd_event;
     701
     702        terminal_queue_cons_event(term, &event);
     703}
     704
     705/** Handle window position event */
     706static void terminal_pos_event(ui_window_t *window, void *arg, pos_event_t *event)
     707{
     708        cons_event_t cevent;
     709        terminal_t *term = (terminal_t *) arg;
     710
     711        sysarg_t sx = -term->off.x;
     712        sysarg_t sy = -term->off.y;
     713
     714        if (event->type == POS_PRESS) {
     715                cevent.type = CEV_POS;
     716                cevent.ev.pos.type = event->type;
     717                cevent.ev.pos.pos_id = event->pos_id;
     718                cevent.ev.pos.btn_num = event->btn_num;
     719
     720                cevent.ev.pos.hpos = (event->hpos - sx) / FONT_WIDTH;
     721                cevent.ev.pos.vpos = (event->vpos - sy) / FONT_SCANLINES;
     722                terminal_queue_cons_event(term, &cevent);
     723        }
     724}
     725
     726/** Handle window unfocus event. */
     727static void terminal_unfocus_event(ui_window_t *window, void *arg)
     728{
     729        terminal_t *term = (terminal_t *) arg;
     730
     731        term->is_focused = false;
     732        term_update(term);
     733}
     734
     735static void term_connection(ipc_call_t *icall, void *arg)
     736{
     737        terminal_t *term = NULL;
     738
     739        list_foreach(terms, link, terminal_t, cur) {
     740                if (cur->dsid == (service_id_t) ipc_get_arg2(icall)) {
     741                        term = cur;
     742                        break;
     743                }
     744        }
     745
     746        if (term == NULL) {
     747                async_answer_0(icall, ENOENT);
     748                return;
     749        }
     750
     751        if (!atomic_flag_test_and_set(&term->refcnt))
     752                chargrid_set_cursor_visibility(term->frontbuf, true);
     753
     754        con_conn(icall, &term->srvs);
     755}
     756
     757errno_t terminal_create(const char *display_spec, sysarg_t width,
     758    sysarg_t height, terminal_flags_t flags, terminal_t **rterm)
     759{
     760        terminal_t *term;
     761        gfx_bitmap_params_t params;
     762        ui_wnd_params_t wparams;
     763        gfx_rect_t rect;
     764        gfx_coord2_t off;
     765        gfx_rect_t wrect;
     766        errno_t rc;
     767
     768        term = calloc(1, sizeof(terminal_t));
     769        if (term == NULL) {
     770                printf("Out of memory.\n");
    672771                return ENOMEM;
    673772        }
    674773
    675         term->ucols = cols;
    676         term->urows = rows;
    677         term->ubuf = buf;
    678 
    679         /* Scroll back to active screen. */
    680         termui_history_scroll(term->termui, INT_MAX);
    681 
    682         fibril_mutex_unlock(&term->mtx);
    683 
    684         *rbuf = buf;
    685         return EOK;
    686 }
    687 
    688 /** Delete shared buffer.
    689  *
    690  * @param srv Console server
    691  */
    692 static void term_unmap(con_srv_t *srv)
    693 {
    694         terminal_t *term = srv_to_terminal(srv);
    695         void *buf;
    696 
    697         fibril_mutex_lock(&term->mtx);
    698 
    699         buf = term->ubuf;
    700         term->ubuf = NULL;
    701 
    702         termui_wipe_screen(term->termui, 0);
    703 
    704         fibril_mutex_unlock(&term->mtx);
    705 
    706         /* Update terminal */
    707         term_render(term);
    708         gfx_update(term->gc);
    709 
    710         if (buf != NULL)
    711                 as_area_destroy(buf);
    712 }
    713 
    714 /** Update area of terminal from shared buffer.
    715  *
    716  * @param srv Console server
    717  * @param c0 Column coordinate of top-left corner (inclusive)
    718  * @param r0 Row coordinate of top-left corner (inclusive)
    719  * @param c1 Column coordinate of bottom-right corner (exclusive)
    720  * @param r1 Row coordinate of bottom-right corner (exclusive)
    721  */
    722 static void term_buf_update(con_srv_t *srv, sysarg_t c0, sysarg_t r0,
    723     sysarg_t c1, sysarg_t r1)
    724 {
    725         terminal_t *term = srv_to_terminal(srv);
    726         bool cursor;
    727 
    728         fibril_mutex_lock(&term->mtx);
    729 
    730         if (term->ubuf == NULL) {
    731                 fibril_mutex_unlock(&term->mtx);
    732                 return;
    733         }
    734 
    735         /* Make sure we have meaningful coordinates, within bounds */
    736         c1 = min(c1, term->ucols);
    737         c1 = min(c1, (sysarg_t) termui_get_cols(term->termui));
    738         r1 = min(r1, term->urows);
    739         r1 = min(r1, (sysarg_t) termui_get_rows(term->termui));
    740 
    741         if (c0 >= c1 || r0 >= r1) {
    742                 fibril_mutex_unlock(&term->mtx);
    743                 return;
    744         }
    745 
    746         /* Update front buffer from user buffer */
    747 
    748         for (sysarg_t row = r0; row < r1; row++) {
    749                 termui_cell_t *cells = termui_get_active_row(term->termui, row);
    750 
    751                 for (sysarg_t col = c0; col < c1; col++) {
    752                         cursor = cells[col].cursor;
    753                         cells[col] = charfield_to_termui_cell(term, &term->ubuf[row * term->ucols + col]);
    754                         cells[col].cursor = cursor;
    755                 }
    756 
    757                 termui_update_cb(term, c0, row, &cells[c0], c1 - c0);
    758         }
    759 
    760         fibril_mutex_unlock(&term->mtx);
    761 
    762         /* Update terminal */
    763         term_render(term);
    764         gfx_update(term->gc);
    765 }
    766 
    767 static errno_t terminal_window_resize(terminal_t *term)
    768 {
    769         gfx_rect_t rect;
    770         ui_window_get_app_rect(term->window, &rect);
    771 
    772         int width = rect.p1.x - rect.p0.x;
    773         int height = rect.p1.y - rect.p0.y;
    774 
    775         if (!term->gc)
    776                 term->gc = ui_window_get_gc(term->window);
    777         else
    778                 assert(term->gc == ui_window_get_gc(term->window));
    779 
    780         if (!term->ui_res)
    781                 term->ui_res = ui_window_get_res(term->window);
    782         else
    783                 assert(term->ui_res == ui_window_get_res(term->window));
    784 
    785         gfx_bitmap_t *new_bmp;
    786         gfx_bitmap_params_t params;
     774        link_initialize(&term->link);
     775        fibril_mutex_initialize(&term->mtx);
     776        atomic_flag_clear(&term->refcnt);
     777
     778        prodcons_initialize(&term->input_pc);
     779        term->char_remains_len = 0;
     780
     781        term->w = width;
     782        term->h = height;
     783
     784        term->cols = width / FONT_WIDTH;
     785        term->rows = height / FONT_SCANLINES;
     786
     787        term->frontbuf = NULL;
     788        term->backbuf = NULL;
     789
     790        term->frontbuf = chargrid_create(term->cols, term->rows,
     791            CHARGRID_FLAG_NONE);
     792        if (!term->frontbuf) {
     793                printf("Error creating front buffer.\n");
     794                rc = ENOMEM;
     795                goto error;
     796        }
     797
     798        term->backbuf = chargrid_create(term->cols, term->rows,
     799            CHARGRID_FLAG_NONE);
     800        if (!term->backbuf) {
     801                printf("Error creating back buffer.\n");
     802                rc = ENOMEM;
     803                goto error;
     804        }
     805
     806        rect.p0.x = 0;
     807        rect.p0.y = 0;
     808        rect.p1.x = width;
     809        rect.p1.y = height;
     810
     811        ui_wnd_params_init(&wparams);
     812        wparams.caption = "Terminal";
     813        if ((flags & tf_topleft) != 0)
     814                wparams.placement = ui_wnd_place_top_left;
     815
     816        /*
     817         * Compute window rectangle such that application area corresponds
     818         * to rect
     819         */
     820        ui_wdecor_rect_from_app(wparams.style, &rect, &wrect);
     821        off = wrect.p0;
     822        gfx_rect_rtranslate(&off, &wrect, &wparams.rect);
     823
     824        term->off = off;
     825
     826        rc = ui_create(display_spec, &term->ui);
     827        if (rc != EOK) {
     828                printf("Error creating UI on %s.\n", display_spec);
     829                goto error;
     830        }
     831
     832        rc = ui_window_create(term->ui, &wparams, &term->window);
     833        if (rc != EOK) {
     834                printf("Error creating window.\n");
     835                goto error;
     836        }
     837
     838        term->gc = ui_window_get_gc(term->window);
     839        term->ui_res = ui_window_get_res(term->window);
     840
     841        ui_window_set_cb(term->window, &terminal_window_cb, (void *) term);
     842
    787843        gfx_bitmap_params_init(&params);
    788844        params.rect.p0.x = 0;
     
    791847        params.rect.p1.y = height;
    792848
    793         errno_t rc = gfx_bitmap_create(term->gc, &params, NULL, &new_bmp);
     849        rc = gfx_bitmap_create(term->gc, &params, NULL, &term->bmp);
    794850        if (rc != EOK) {
    795                 fprintf(stderr, "Error allocating new screen bitmap: %s\n", str_error(rc));
    796                 return rc;
    797         }
    798 
    799         if (term->bmp) {
    800                 rc = gfx_bitmap_destroy(term->bmp);
    801                 if (rc != EOK)
    802                         fprintf(stderr, "Error deallocating old screen bitmap: %s\n", str_error(rc));
    803         }
    804 
    805         term->bmp = new_bmp;
    806         term->w = width;
    807         term->h = height;
    808 
    809         term_clear_bitmap(term, termui_color_to_pixel(term->default_bgcolor));
    810 
    811         return EOK;
    812 }
    813 
    814 void terminal_destroy(terminal_t *term)
    815 {
    816         list_remove(&term->link);
    817 
    818         termui_destroy(term->termui);
    819 
    820         if (term->ubuf)
    821                 as_area_destroy(term->ubuf);
    822 
    823         ui_destroy(term->ui);
    824         free(term);
    825 }
    826 
    827 static void terminal_queue_cons_event(terminal_t *term, cons_event_t *ev)
    828 {
    829         /* Got key press/release event */
    830         terminal_event_t *event =
    831             (terminal_event_t *) malloc(sizeof(terminal_event_t));
    832         if (event == NULL)
    833                 return;
    834 
    835         event->ev = *ev;
    836         link_initialize(&event->link);
    837 
    838         prodcons_produce(&term->input_pc, &event->link);
    839 }
    840 
    841 /** Handle window close event. */
    842 static void terminal_close_event(ui_window_t *window, void *arg)
    843 {
    844         terminal_t *term = (terminal_t *) arg;
    845 
    846         ui_quit(term->ui);
    847 }
    848 
    849 /** Handle window focus event. */
    850 static void terminal_focus_event(ui_window_t *window, void *arg,
    851     unsigned nfocus)
    852 {
    853         terminal_t *term = (terminal_t *) arg;
    854 
    855         (void)nfocus;
    856         term->is_focused = true;
    857         term_render(term);
    858         gfx_update(term->gc);
    859 }
    860 
    861 static void terminal_resize_handler(ui_window_t *window, void *arg)
    862 {
    863         terminal_t *term = (terminal_t *) arg;
    864 
    865         fibril_mutex_lock(&term->mtx);
    866 
    867         errno_t rc = terminal_window_resize(term);
    868         if (rc == EOK) {
    869                 (void) termui_resize(term->termui, term->w / FONT_WIDTH, term->h / FONT_SCANLINES, SCROLLBACK_MAX_LINES);
    870                 termui_refresh_cb(term);
    871                 term_render(term);
    872                 gfx_update(term->gc);
    873 
    874                 cons_event_t event = { .type = CEV_RESIZE };
    875                 terminal_queue_cons_event(term, &event);
    876         }
    877 
    878         fibril_mutex_unlock(&term->mtx);
    879 }
    880 
    881 static void terminal_resize_event(ui_window_t *window, void *arg)
    882 {
    883         ui_window_def_resize(window);
    884         terminal_resize_handler(window, arg);
    885 }
    886 
    887 static void terminal_maximize_event(ui_window_t *window, void *arg)
    888 {
    889         ui_window_def_maximize(window);
    890         terminal_resize_handler(window, arg);
    891 }
    892 
    893 static void terminal_unmaximize_event(ui_window_t *window, void *arg)
    894 {
    895         ui_window_def_unmaximize(window);
    896         terminal_resize_handler(window, arg);
    897 }
    898 
    899 /** Handle window keyboard event */
    900 static void terminal_kbd_event(ui_window_t *window, void *arg,
    901     kbd_event_t *kbd_event)
    902 {
    903         terminal_t *term = (terminal_t *) arg;
    904         cons_event_t event;
    905 
    906         event.type = CEV_KEY;
    907         event.ev.key = *kbd_event;
    908 
    909         const int PAGE_ROWS = (termui_get_rows(term->termui) * 2) / 3;
    910 
    911         fibril_mutex_lock(&term->mtx);
    912 
    913         if (!term->ubuf && kbd_event->type == KEY_PRESS &&
    914             (kbd_event->key == KC_PAGE_UP || kbd_event->key == KC_PAGE_DOWN)) {
    915 
    916                 termui_history_scroll(term->termui,
    917                     (kbd_event->key == KC_PAGE_UP) ? -PAGE_ROWS : PAGE_ROWS);
    918 
    919                 term_render(term);
    920                 gfx_update(term->gc);
    921         } else {
    922                 terminal_queue_cons_event(term, &event);
    923         }
    924 
    925         fibril_mutex_unlock(&term->mtx);
    926 }
    927 
    928 /** Handle window position event */
    929 static void terminal_pos_event(ui_window_t *window, void *arg, pos_event_t *event)
    930 {
    931         cons_event_t cevent;
    932         terminal_t *term = (terminal_t *) arg;
    933 
    934         switch (event->type) {
    935         case POS_UPDATE:
    936                 return;
    937 
    938         case POS_PRESS:
    939         case POS_RELEASE:
    940         case POS_DCLICK:
    941         }
    942 
    943         /* Ignore mouse events when we're in scrollback mode. */
    944         if (termui_scrollback_is_active(term->termui))
    945                 return;
    946 
    947         sysarg_t sx = term->off.x;
    948         sysarg_t sy = term->off.y;
    949 
    950         if (event->hpos < sx || event->vpos < sy)
    951                 return;
    952 
    953         cevent.type = CEV_POS;
    954         cevent.ev.pos.type = event->type;
    955         cevent.ev.pos.pos_id = event->pos_id;
    956         cevent.ev.pos.btn_num = event->btn_num;
    957 
    958         cevent.ev.pos.hpos = (event->hpos - sx) / FONT_WIDTH;
    959         cevent.ev.pos.vpos = (event->vpos - sy) / FONT_SCANLINES;
    960 
    961         /* Filter out events outside the terminal area. */
    962         int cols = termui_get_cols(term->termui);
    963         int rows = termui_get_rows(term->termui);
    964 
    965         if (cevent.ev.pos.hpos < (sysarg_t) cols && cevent.ev.pos.vpos < (sysarg_t) rows)
    966                 terminal_queue_cons_event(term, &cevent);
    967 }
    968 
    969 /** Handle window unfocus event. */
    970 static void terminal_unfocus_event(ui_window_t *window, void *arg,
    971     unsigned nfocus)
    972 {
    973         terminal_t *term = (terminal_t *) arg;
    974 
    975         if (nfocus == 0) {
    976                 term->is_focused = false;
    977                 term_render(term);
    978                 gfx_update(term->gc);
    979         }
    980 }
    981 
    982 static void term_connection(ipc_call_t *icall, void *arg)
    983 {
    984         terminal_t *term = NULL;
    985 
    986         list_foreach(terms, link, terminal_t, cur) {
    987                 if (cur->dsid == (service_id_t) ipc_get_arg2(icall)) {
    988                         term = cur;
    989                         break;
    990                 }
    991         }
    992 
    993         if (term == NULL) {
    994                 async_answer_0(icall, ENOENT);
    995                 return;
    996         }
    997 
    998         if (!atomic_flag_test_and_set(&term->refcnt))
    999                 termui_set_cursor_visibility(term->termui, true);
    1000 
    1001         con_conn(icall, &term->srvs);
    1002 }
    1003 
    1004 static errno_t term_init_window(terminal_t *term, const char *display_spec,
    1005     gfx_coord_t width, gfx_coord_t height,
    1006     gfx_coord_t min_width, gfx_coord_t min_height,
    1007     terminal_flags_t flags)
    1008 {
    1009         gfx_rect_t min_rect = { { 0, 0 }, { min_width, min_height } };
    1010         gfx_rect_t wmin_rect;
    1011         gfx_rect_t wrect;
    1012 
    1013         errno_t rc = ui_create(display_spec, &term->ui);
    1014         if (rc != EOK) {
    1015                 printf("Error creating UI on %s.\n", display_spec);
    1016                 return rc;
    1017         }
    1018 
    1019         ui_wnd_params_t wparams;
    1020         ui_wnd_params_init(&wparams);
    1021         wparams.caption = "Terminal";
    1022         wparams.style |= ui_wds_maximize_btn | ui_wds_resizable;
    1023 
    1024         if ((flags & tf_topleft) != 0)
    1025                 wparams.placement = ui_wnd_place_top_left;
    1026 
    1027         if (ui_is_fullscreen(term->ui)) {
    1028                 wparams.placement = ui_wnd_place_full_screen;
    1029                 wparams.style &= ~ui_wds_decorated;
    1030         }
    1031 
    1032         /* Compute wrect such that application area corresponds to rect. */
    1033         ui_wdecor_rect_from_app(term->ui, wparams.style, &min_rect, &wrect);
    1034         gfx_rect_rtranslate(&wrect.p0, &wrect, &wmin_rect);
    1035         wparams.min_size = wmin_rect.p1;
    1036 
    1037         gfx_rect_t rect = { { 0, 0 }, { width, height } };
    1038         ui_wdecor_rect_from_app(term->ui, wparams.style, &rect, &rect);
    1039         term->off.x = -rect.p0.x;
    1040         term->off.y = -rect.p0.y;
    1041         printf("off=%d,%d\n", term->off.x, term->off.y);
    1042         gfx_rect_translate(&term->off, &rect, &wparams.rect);
    1043         printf("wparams.rect=%d,%d,%d,%d\n",
    1044             wparams.rect.p0.x,
    1045             wparams.rect.p1.x,
    1046             wparams.rect.p0.y,
    1047             wparams.rect.p1.y);
    1048 
    1049         rc = ui_window_create(term->ui, &wparams, &term->window);
    1050         if (rc != EOK)
    1051                 return rc;
    1052 
    1053         ui_window_set_cb(term->window, &terminal_window_cb, (void *) term);
    1054         return terminal_window_resize(term);
    1055 }
    1056 
    1057 errno_t terminal_create(const char *display_spec, sysarg_t width,
    1058     sysarg_t height, terminal_flags_t flags, const char *command,
    1059     terminal_t **rterm)
    1060 {
    1061         errno_t rc;
    1062 
    1063         terminal_t *term = calloc(1, sizeof(terminal_t));
    1064         if (term == NULL) {
    1065                 printf("Out of memory.\n");
    1066                 return ENOMEM;
    1067         }
    1068 
    1069         link_initialize(&term->link);
    1070         fibril_mutex_initialize(&term->mtx);
    1071         atomic_flag_clear(&term->refcnt);
    1072 
    1073         prodcons_initialize(&term->input_pc);
    1074         term->char_remains_len = 0;
    1075 
    1076         term->default_bgcolor = termui_color_from_pixel(_basic_colors[COLOR_WHITE | COLOR_BRIGHT]);
    1077         term->default_fgcolor = termui_color_from_pixel(_basic_colors[COLOR_BLACK]);
    1078 
    1079         term->emphasis_bgcolor = termui_color_from_pixel(_basic_colors[COLOR_WHITE | COLOR_BRIGHT]);
    1080         term->emphasis_fgcolor = termui_color_from_pixel(_basic_colors[COLOR_RED | COLOR_BRIGHT]);
    1081 
    1082         term->selection_bgcolor = termui_color_from_pixel(_basic_colors[COLOR_RED | COLOR_BRIGHT]);
    1083         term->selection_fgcolor = termui_color_from_pixel(_basic_colors[COLOR_WHITE | COLOR_BRIGHT]);
    1084 
    1085         rc = term_init_window(term, display_spec, width, height,
    1086             MIN_WINDOW_COLS * FONT_WIDTH, MIN_WINDOW_ROWS * FONT_SCANLINES, flags);
    1087         if (rc != EOK) {
    1088                 printf("Error creating window (%s).\n", str_error(rc));
     851                printf("Error allocating screen bitmap.\n");
    1089852                goto error;
    1090853        }
    1091854
    1092         term->termui = termui_create(term->w / FONT_WIDTH,
    1093             term->h / FONT_SCANLINES, SCROLLBACK_MAX_LINES);
    1094         if (!term->termui) {
    1095                 printf("Error creating terminal UI.\n");
    1096                 rc = ENOMEM;
    1097                 goto error;
    1098         }
    1099 
    1100         termui_set_refresh_cb(term->termui, termui_refresh_cb, term);
    1101         termui_set_scroll_cb(term->termui, termui_scroll_cb, term);
    1102         termui_set_update_cb(term->termui, termui_update_cb, term);
     855        chargrid_clear(term->frontbuf);
     856        chargrid_clear(term->backbuf);
     857        term->top_row = 0;
    1103858
    1104859        async_set_fallback_port_handler(term_connection, NULL);
     
    1107862        term->srvs.sarg = term;
    1108863
    1109         rc = loc_server_register(NAME, &term->srv);
     864        rc = loc_server_register(NAME);
    1110865        if (rc != EOK) {
    1111866                printf("Error registering server.\n");
     
    1118873            task_get_id());
    1119874
    1120         rc = loc_service_register(term->srv, vc, fallback_port_id, &term->dsid);
     875        rc = loc_service_register(vc, &term->dsid);
    1121876        if (rc != EOK) {
    1122877                printf("Error registering service.\n");
     
    1126881
    1127882        list_append(&term->link, &terms);
    1128         rc = getterm(&term->wait, vc, command);
    1129         if (rc != EOK)
    1130                 goto error;
    1131 
    1132         term->wfid = fibril_create(terminal_wait_fibril, term);
    1133         if (term->wfid == 0)
    1134                 goto error;
    1135 
    1136         fibril_add_ready(term->wfid);
     883        getterm(vc, "/app/bdsh");
    1137884
    1138885        term->is_focused = true;
    1139886
    1140         termui_refresh_cb(term);
     887        term->update.p0.x = 0;
     888        term->update.p0.y = 0;
     889        term->update.p1.x = 0;
     890        term->update.p1.y = 0;
     891
     892        term_repaint(term);
    1141893
    1142894        *rterm = term;
    1143895        return EOK;
    1144896error:
    1145         if (term->dsid != 0)
    1146                 loc_service_unregister(term->srv, term->dsid);
    1147         if (term->srv != NULL)
    1148                 loc_server_unregister(term->srv);
    1149897        if (term->window != NULL)
    1150898                ui_window_destroy(term->window);
    1151899        if (term->ui != NULL)
    1152900                ui_destroy(term->ui);
    1153         if (term->termui != NULL)
    1154                 termui_destroy(term->termui);
     901        if (term->frontbuf != NULL)
     902                chargrid_destroy(term->frontbuf);
     903        if (term->backbuf != NULL)
     904                chargrid_destroy(term->backbuf);
    1155905        free(term);
    1156906        return rc;
    1157907}
    1158908
    1159 static errno_t terminal_wait_fibril(void *arg)
    1160 {
    1161         terminal_t *term = (terminal_t *)arg;
    1162         task_exit_t texit;
    1163         int retval;
    1164 
    1165         /*
    1166          * XXX There is no way to break the sleep if the task does not
    1167          * exit.
    1168          */
    1169         (void) task_wait(&term->wait, &texit, &retval);
    1170         ui_quit(term->ui);
    1171         return EOK;
    1172 }
    1173 
    1174909/** @}
    1175910 */
Note: See TracChangeset for help on using the changeset viewer.