Subversion Repositories HelenOS

Rev

Rev 4347 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (c) 2006 Ondrej Palkovsky
  3.  * Copyright (c) 2008 Martin Decky
  4.  * Copyright (c) 2008 Pavel Rimsky
  5.  * All rights reserved.
  6.  *
  7.  * Redistribution and use in source and binary forms, with or without
  8.  * modification, are permitted provided that the following conditions
  9.  * are met:
  10.  *
  11.  * - Redistributions of source code must retain the above copyright
  12.  *   notice, this list of conditions and the following disclaimer.
  13.  * - Redistributions in binary form must reproduce the above copyright
  14.  *   notice, this list of conditions and the following disclaimer in the
  15.  *   documentation and/or other materials provided with the distribution.
  16.  * - The name of the author may not be used to endorse or promote products
  17.  *   derived from this software without specific prior written permission.
  18.  *
  19.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  20.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  21.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  22.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  23.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  24.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  28.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29.  */
  30.  
  31. /**
  32.  * @defgroup serial Serial console
  33.  * @brief    Serial console services (putc, puts, clear screen, cursor goto,...)
  34.  * @{
  35.  */
  36.  
  37. /** @file
  38.  */
  39.  
  40. #include <stdio.h>
  41. #include <ipc/ipc.h>
  42. #include <async.h>
  43. #include <ipc/fb.h>
  44. #include <bool.h>
  45. #include <errno.h>
  46. #include <console/color.h>
  47. #include <console/style.h>
  48.  
  49. #include "../console/screenbuffer.h"
  50. #include "main.h"
  51. #include "serial_console.h"
  52.  
  53. #define MAX_CONTROL 20
  54.  
  55. static void serial_sgr(const unsigned int mode);
  56. void serial_putchar(wchar_t ch);
  57.  
  58. static int scr_width;
  59. static int scr_height;
  60. static bool color = true;   /** True if producing color output. */
  61. static bool utf8 = false;   /** True if producing UTF8 output. */
  62. static putc_function_t putc_function;
  63.  
  64. /* Allow only 1 connection */
  65. static int client_connected = 0;
  66.  
  67. enum sgr_color_index {
  68.     CI_BLACK    = 0,
  69.     CI_RED      = 1,
  70.     CI_GREEN    = 2,
  71.     CI_BROWN    = 3,
  72.     CI_BLUE     = 4,
  73.     CI_MAGENTA  = 5,
  74.     CI_CYAN     = 6,
  75.     CI_WHITE    = 7,
  76. };
  77.  
  78. enum sgr_command {
  79.     SGR_RESET   = 0,
  80.     SGR_BOLD    = 1,
  81.     SGR_BLINK   = 5,
  82.     SGR_REVERSE = 7,
  83.     SGR_NORMAL_INT  = 22,
  84.     SGR_BLINK_OFF   = 25,
  85.     SGR_REVERSE_OFF = 27,
  86.     SGR_FGCOLOR = 30,
  87.     SGR_BGCOLOR = 40
  88. };
  89.  
  90. static int color_map[] = {
  91.     [COLOR_BLACK]   = CI_BLACK,
  92.     [COLOR_BLUE]    = CI_RED,
  93.     [COLOR_GREEN]   = CI_GREEN,
  94.     [COLOR_CYAN]    = CI_CYAN,
  95.     [COLOR_RED] = CI_RED,
  96.     [COLOR_MAGENTA] = CI_MAGENTA,
  97.     [COLOR_YELLOW]  = CI_BROWN,
  98.     [COLOR_WHITE]   = CI_WHITE
  99. };
  100.  
  101. void serial_puts(char *str)
  102. {
  103.     while (*str)
  104.         putc_function(*(str++));
  105. }
  106.  
  107. void serial_putchar(wchar_t ch)
  108. {
  109.     uint8_t buf[STR_BOUNDS(1)];
  110.     size_t offs;
  111.     size_t i;
  112.  
  113.     if (utf8 != true) {
  114.         if (ch >= 0 && ch < 128)
  115.             (*putc_function)((uint8_t) ch);
  116.         else
  117.             (*putc_function)('?');
  118.         return;
  119.     }
  120.  
  121.     offs = 0;
  122.     if (chr_encode(ch, buf, &offs, STR_BOUNDS(1)) == EOK) {
  123.         for (i = 0; i < offs; i++)
  124.             (*putc_function)(buf[i]);
  125.     } else {
  126.         (*putc_function)('?');
  127.     }
  128.  
  129. }
  130.  
  131. void serial_goto(const unsigned int row, const unsigned int col)
  132. {
  133.     if ((row > scr_height) || (col > scr_width))
  134.         return;
  135.    
  136.     char control[MAX_CONTROL];
  137.     snprintf(control, MAX_CONTROL, "\033[%u;%uf", row + 1, col + 1);
  138.     serial_puts(control);
  139. }
  140.  
  141. void serial_clrscr(void)
  142. {
  143.     /* Initialize graphic rendition attributes. */
  144.     serial_sgr(SGR_RESET);
  145.     if (color) {
  146.         serial_sgr(SGR_FGCOLOR + CI_BLACK);
  147.         serial_sgr(SGR_BGCOLOR + CI_WHITE);
  148.     }
  149.  
  150.     serial_puts("\033[2J");
  151. }
  152.  
  153. void serial_scroll(int i)
  154. {
  155.     if (i > 0) {
  156.         serial_goto(scr_height - 1, 0);
  157.         while (i--)
  158.             serial_puts("\033D");
  159.     } else if (i < 0) {
  160.         serial_goto(0, 0);
  161.         while (i++)
  162.             serial_puts("\033M");
  163.     }
  164. }
  165.  
  166. /** ECMA-48 Set Graphics Rendition. */
  167. static void serial_sgr(const unsigned int mode)
  168. {
  169.     char control[MAX_CONTROL];
  170.     snprintf(control, MAX_CONTROL, "\033[%um", mode);
  171.     serial_puts(control);
  172. }
  173.  
  174. /** Set scrolling region. */
  175. void serial_set_scroll_region(unsigned last_row)
  176. {
  177.     char control[MAX_CONTROL];
  178.     snprintf(control, MAX_CONTROL, "\033[0;%ur", last_row);
  179.     serial_puts(control);
  180. }
  181.  
  182. void serial_cursor_disable(void)
  183. {
  184.     serial_puts("\033[?25l");
  185. }
  186.  
  187. void serial_cursor_enable(void)
  188. {
  189.     serial_puts("\033[?25h");
  190. }
  191.  
  192. void serial_console_init(putc_function_t putc_fn, uint32_t w, uint32_t h)
  193. {
  194.     scr_width = w;
  195.     scr_height = h;
  196.     putc_function = putc_fn;
  197. }
  198.  
  199. static void serial_set_style(int style)
  200. {
  201.     if (style == STYLE_EMPHASIS) {
  202.         if (color) {
  203.             serial_sgr(SGR_RESET);
  204.             serial_sgr(SGR_FGCOLOR + CI_RED);
  205.             serial_sgr(SGR_BGCOLOR + CI_WHITE);
  206.         }
  207.         serial_sgr(SGR_BOLD);
  208.     } else {
  209.         if (color) {
  210.             serial_sgr(SGR_RESET);
  211.             serial_sgr(SGR_FGCOLOR + CI_BLACK);
  212.             serial_sgr(SGR_BGCOLOR + CI_WHITE);
  213.         }
  214.         serial_sgr(SGR_NORMAL_INT);
  215.     }
  216. }
  217.  
  218. static void serial_set_idx(unsigned fgcolor, unsigned bgcolor,
  219.     unsigned flags)
  220. {
  221.     if (color) {
  222.         serial_sgr(SGR_RESET);
  223.         serial_sgr(SGR_FGCOLOR + color_map[fgcolor]);
  224.         serial_sgr(SGR_BGCOLOR + color_map[bgcolor]);
  225.     } else {
  226.         if (fgcolor < bgcolor)
  227.             serial_sgr(SGR_RESET);
  228.         else
  229.             serial_sgr(SGR_REVERSE);
  230.     }  
  231. }
  232.  
  233. static void serial_set_rgb(uint32_t fgcolor, uint32_t bgcolor)
  234. {
  235.     if (fgcolor < bgcolor)
  236.         serial_sgr(SGR_REVERSE_OFF);
  237.     else
  238.         serial_sgr(SGR_REVERSE);   
  239. }
  240.  
  241. static void serial_set_attrs(const attrs_t *a)
  242. {
  243.     switch (a->t) {
  244.     case at_style: serial_set_style(a->a.s.style); break;
  245.     case at_rgb: serial_set_rgb(a->a.r.fg_color, a->a.r.bg_color); break;
  246.     case at_idx: serial_set_idx(a->a.i.fg_color,
  247.         a->a.i.bg_color, a->a.i.flags); break;
  248.     default: break;
  249.     }
  250. }
  251.  
  252. /** Draw text data to viewport.
  253.  *
  254.  * @param vport Viewport id
  255.  * @param data  Text data.
  256.  * @param x Leftmost column of the area.
  257.  * @param y Topmost row of the area.
  258.  * @param w Number of rows.
  259.  * @param h Number of columns.
  260.  */
  261. static void draw_text_data(keyfield_t *data, unsigned int x,
  262.     unsigned int y, unsigned int w, unsigned int h)
  263. {
  264.     unsigned int i, j;
  265.     keyfield_t *field;
  266.     attrs_t *a0, *a1;
  267.  
  268.     serial_goto(y, x);
  269.     a0 = &data[0].attrs;
  270.     serial_set_attrs(a0);
  271.  
  272.     for (j = 0; j < h; j++) {
  273.         if (j > 0 && w != scr_width)
  274.             serial_goto(y, x);
  275.  
  276.         for (i = 0; i < w; i++) {
  277.             field = &data[j * w + i];
  278.  
  279.             a1 = &field->attrs;
  280.             if (!attrs_same(*a0, *a1))
  281.                 serial_set_attrs(a1);
  282.             serial_putchar(field->character);
  283.             a0 = a1;
  284.         }
  285.     }
  286. }
  287.  
  288. int lastcol = 0;
  289. int lastrow = 0;
  290.  
  291. /**
  292.  * Main function of the thread serving client connections.
  293.  */
  294. void serial_client_connection(ipc_callid_t iid, ipc_call_t *icall)
  295. {
  296.     int retval;
  297.     ipc_callid_t callid;
  298.     ipc_call_t call;
  299.     keyfield_t *interbuf = NULL;
  300.     size_t intersize = 0;
  301.  
  302.     wchar_t c;
  303.     int col, row, w, h;
  304.     int i;
  305.  
  306.     attrs_t cur_attr;
  307.    
  308.     if (client_connected) {
  309.         ipc_answer_0(iid, ELIMIT);
  310.         return;
  311.     }
  312.    
  313.     client_connected = 1;
  314.     ipc_answer_0(iid, EOK);
  315.  
  316.     cur_attr.t = at_style;
  317.     cur_attr.a.s.style = STYLE_NORMAL;
  318.    
  319.     /* Clear the terminal, set scrolling region
  320.        to 0 - height rows. */
  321.     serial_clrscr();
  322.     serial_goto(0, 0);
  323.     serial_set_scroll_region(scr_height);
  324.    
  325.     while (true) {
  326.         callid = async_get_call(&call);
  327.         switch (IPC_GET_METHOD(call)) {
  328.         case IPC_M_PHONE_HUNGUP:
  329.             client_connected = 0;
  330.             ipc_answer_0(callid, EOK);
  331.             return;
  332.         case IPC_M_SHARE_OUT:
  333.             /* We accept one area for data interchange */
  334.             intersize = IPC_GET_ARG2(call);
  335.             if (intersize >= scr_width * scr_height *
  336.                 sizeof(*interbuf)) {
  337.                 receive_comm_area(callid, &call,
  338.                     (void *) &interbuf);
  339.                 continue;
  340.             }
  341.             retval = EINVAL;
  342.             break;
  343.         case FB_DRAW_TEXT_DATA:
  344.             col = IPC_GET_ARG1(call);
  345.             row = IPC_GET_ARG2(call);
  346.             w = IPC_GET_ARG3(call);
  347.             h = IPC_GET_ARG4(call);
  348.             if (!interbuf) {
  349.                 retval = EINVAL;
  350.                 break;
  351.             }
  352.             if (col + w > scr_width || row + h > scr_height) {
  353.                 retval = EINVAL;
  354.                 break;
  355.             }
  356.             draw_text_data(interbuf, col, row, w, h);
  357.             lastrow = row + h - 1;
  358.             lastcol = col + w;
  359.             retval = 0;
  360.             break;
  361.         case FB_PUTCHAR:
  362.             c = IPC_GET_ARG1(call);
  363.             row = IPC_GET_ARG2(call);
  364.             col = IPC_GET_ARG3(call);
  365.             if ((lastcol != col) || (lastrow != row))
  366.                 serial_goto(row, col);
  367.             lastcol = col + 1;
  368.             lastrow = row;
  369.             serial_putchar(c);
  370.             retval = 0;
  371.             break;
  372.         case FB_CURSOR_GOTO:
  373.             row = IPC_GET_ARG1(call);
  374.             col = IPC_GET_ARG2(call);
  375.             serial_goto(row, col);
  376.             lastrow = row;
  377.             lastcol = col;
  378.             retval = 0;
  379.             break;
  380.         case FB_GET_CSIZE:
  381.             ipc_answer_2(callid, EOK, scr_height, scr_width);
  382.             continue;
  383.         case FB_CLEAR:
  384.             serial_clrscr();
  385.             retval = 0;
  386.             break;
  387.         case FB_SET_STYLE:
  388.             cur_attr.t = at_style;
  389.             cur_attr.a.s.style = IPC_GET_ARG1(call);
  390.             cur_attr.a.i.bg_color = IPC_GET_ARG2(call);
  391.             serial_set_attrs(&cur_attr);
  392.  
  393.             retval = 0;
  394.             break;
  395.         case FB_SET_COLOR:
  396.             cur_attr.t = at_idx;
  397.             cur_attr.a.i.fg_color = IPC_GET_ARG1(call);
  398.             cur_attr.a.i.bg_color = IPC_GET_ARG2(call);
  399.             cur_attr.a.i.flags = IPC_GET_ARG3(call);
  400.             serial_set_attrs(&cur_attr);
  401.  
  402.             retval = 0;
  403.             break;
  404.         case FB_SET_RGB_COLOR:
  405.             cur_attr.t = at_rgb;
  406.             cur_attr.a.i.fg_color = IPC_GET_ARG1(call);
  407.             cur_attr.a.i.bg_color = IPC_GET_ARG2(call);
  408.             serial_set_attrs(&cur_attr);
  409.  
  410.             retval = 0;
  411.             break;
  412.         case FB_SCROLL:
  413.             i = IPC_GET_ARG1(call);
  414.             if ((i > scr_height) || (i < -scr_height)) {
  415.                 retval = EINVAL;
  416.                 break;
  417.             }
  418.             serial_scroll(i);
  419.             serial_goto(lastrow, lastcol);
  420.             retval = 0;
  421.             break;
  422.         case FB_CURSOR_VISIBILITY:
  423.             if(IPC_GET_ARG1(call))
  424.                 serial_cursor_enable();
  425.             else
  426.                 serial_cursor_disable();
  427.             retval = 0;
  428.             break;
  429.         case FB_SCREEN_YIELD:
  430.             serial_sgr(SGR_RESET);
  431.             serial_puts("\033[2J");
  432.             serial_goto(0, 0);
  433.             serial_cursor_enable();
  434.             retval = 0;
  435.             break;
  436.         case FB_SCREEN_RECLAIM:
  437.             serial_clrscr();
  438.             serial_set_attrs(&cur_attr);
  439.             retval = 0;
  440.             break;
  441.         default:
  442.             retval = ENOENT;
  443.         }
  444.         ipc_answer_0(callid, retval);
  445.     }
  446. }
  447.  
  448. /**
  449.  * @}
  450.  */
  451.