Subversion Repositories HelenOS

Rev

Rev 3742 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (c) 2006 Josef Cejka
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * - Redistributions of source code must retain the above copyright
  10.  *   notice, this list of conditions and the following disclaimer.
  11.  * - Redistributions in binary form must reproduce the above copyright
  12.  *   notice, this list of conditions and the following disclaimer in the
  13.  *   documentation and/or other materials provided with the distribution.
  14.  * - The name of the author may not be used to endorse or promote products
  15.  *   derived from this software without specific prior written permission.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  18.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  */
  28.  
  29. /** @addtogroup console
  30.  * @{
  31.  */
  32. /** @file
  33.  */
  34.  
  35. #include <libc.h>
  36. #include <fb.h>
  37. #include <ipc/ipc.h>
  38. #include <keys.h>
  39. #include <ipc/fb.h>
  40. #include <ipc/services.h>
  41. #include <errno.h>
  42. #include <key_buffer.h>
  43. #include <console.h>
  44. #include <unistd.h>
  45. #include <async.h>
  46. #include <libadt/fifo.h>
  47. #include <screenbuffer.h>
  48. #include <sys/mman.h>
  49. #include <stdio.h>
  50.  
  51. #include "gcons.h"
  52.  
  53. #define MAX_KEYREQUESTS_BUFFERED 32
  54.  
  55. #define NAME "console"
  56.  
  57. /** Index of currently used virtual console.
  58.  */
  59. int active_console = 0;
  60.  
  61. /** Information about framebuffer
  62.  */
  63. struct {
  64.     int phone;      /**< Framebuffer phone */
  65.     ipcarg_t rows;      /**< Framebuffer rows */
  66.     ipcarg_t cols;      /**< Framebuffer columns */
  67. } fb_info;
  68.  
  69. typedef struct {
  70.     keybuffer_t keybuffer;      /**< Buffer for incoming keys. */
  71.     /** Buffer for unsatisfied request for keys. */
  72.     FIFO_CREATE_STATIC(keyrequests, ipc_callid_t,
  73.         MAX_KEYREQUESTS_BUFFERED); 
  74.     int keyrequest_counter;     /**< Number of requests in buffer. */
  75.     int client_phone;       /**< Phone to connected client. */
  76.     int used;           /**< 1 if this virtual console is
  77.                      * connected to some client.*/
  78.     screenbuffer_t screenbuffer;    /**< Screenbuffer for saving screen
  79.                      * contents and related settings. */
  80. } connection_t;
  81.  
  82. static connection_t connections[CONSOLE_COUNT]; /**< Array of data for virtual
  83.                          * consoles */
  84. static keyfield_t *interbuffer = NULL;      /**< Pointer to memory shared
  85.                          * with framebufer used for
  86.                          * faster virtual console
  87.                          * switching */
  88.  
  89.  
  90. /** Find unused virtual console.
  91.  *
  92.  */
  93. static int find_free_connection(void)
  94. {
  95.     int i;
  96.    
  97.     for (i = 0; i < CONSOLE_COUNT; i++) {
  98.         if (!connections[i].used)
  99.             return i;
  100.     }
  101.     return -1;
  102. }
  103.  
  104. static void clrscr(void)
  105. {
  106.     async_msg_0(fb_info.phone, FB_CLEAR);
  107. }
  108.  
  109. static void curs_visibility(bool visible)
  110. {
  111.     async_msg_1(fb_info.phone, FB_CURSOR_VISIBILITY, visible);
  112. }
  113.  
  114. static void curs_hide_sync(void)
  115. {
  116.     ipc_call_sync_1_0(fb_info.phone, FB_CURSOR_VISIBILITY, false);
  117. }
  118.  
  119. static void curs_goto(int row, int col)
  120. {
  121.     async_msg_2(fb_info.phone, FB_CURSOR_GOTO, row, col);
  122. }
  123.  
  124. static void set_style(style_t *style)
  125. {
  126.     async_msg_2(fb_info.phone, FB_SET_STYLE, style->fg_color,
  127.         style->bg_color);
  128. }
  129.  
  130. static void set_style_col(int fgcolor, int bgcolor)
  131. {
  132.     async_msg_2(fb_info.phone, FB_SET_STYLE, fgcolor, bgcolor);
  133. }
  134.  
  135. static void prtchr(char c, int row, int col)
  136. {
  137.     async_msg_3(fb_info.phone, FB_PUTCHAR, c, row, col);
  138. }
  139.  
  140. /** Check key and process special keys.
  141.  *
  142.  *
  143.  */
  144. static void write_char(int console, char key)
  145. {
  146.     screenbuffer_t *scr = &(connections[console].screenbuffer);
  147.    
  148.     switch (key) {
  149.     case '\n':
  150.         scr->position_y++;
  151.         scr->position_x = 0;
  152.         break;
  153.     case '\r':
  154.         break;
  155.     case '\t':
  156.         scr->position_x += 8;
  157.         scr->position_x -= scr->position_x % 8;
  158.         break;
  159.     case '\b':
  160.         if (scr->position_x == 0)
  161.             break;
  162.         scr->position_x--;
  163.         if (console == active_console)
  164.             prtchr(' ', scr->position_y, scr->position_x);
  165.         screenbuffer_putchar(scr, ' ');
  166.         break;
  167.     default:   
  168.         if (console == active_console)
  169.             prtchr(key, scr->position_y, scr->position_x);
  170.  
  171.         screenbuffer_putchar(scr, key);
  172.         scr->position_x++;
  173.     }
  174.    
  175.     scr->position_y += (scr->position_x >= scr->size_x);
  176.    
  177.     if (scr->position_y >= scr->size_y) {
  178.         scr->position_y = scr->size_y - 1;
  179.         screenbuffer_clear_line(scr, scr->top_line);
  180.         scr->top_line = (scr->top_line + 1) % scr->size_y;
  181.         if (console == active_console)
  182.             async_msg_1(fb_info.phone, FB_SCROLL, 1);
  183.     }
  184.    
  185.     scr->position_x = scr->position_x % scr->size_x;
  186.    
  187.     if (console == active_console)
  188.         curs_goto(scr->position_y, scr->position_x);
  189.    
  190. }
  191.  
  192. /** Switch to new console */
  193. static void change_console(int newcons)
  194. {
  195.     connection_t *conn;
  196.     int i, j, rc;
  197.     keyfield_t *field;
  198.     style_t *style;
  199.    
  200.     if (newcons == active_console)
  201.         return;
  202.    
  203.     if (newcons == KERNEL_CONSOLE) {
  204.         async_serialize_start();
  205.         curs_hide_sync();
  206.         gcons_in_kernel();
  207.         async_serialize_end();
  208.        
  209.         if (__SYSCALL0(SYS_DEBUG_ENABLE_CONSOLE))
  210.             active_console = KERNEL_CONSOLE;
  211.         else
  212.             newcons = active_console;
  213.     }
  214.    
  215.     if (newcons != KERNEL_CONSOLE) {
  216.         async_serialize_start();
  217.        
  218.         if (active_console == KERNEL_CONSOLE)
  219.             gcons_redraw_console();
  220.        
  221.         active_console = newcons;
  222.         gcons_change_console(newcons);
  223.         conn = &connections[active_console];
  224.        
  225.         set_style(&conn->screenbuffer.style);
  226.         curs_visibility(false);
  227.         if (interbuffer) {
  228.             for (i = 0; i < conn->screenbuffer.size_x; i++)
  229.                 for (j = 0; j < conn->screenbuffer.size_y; j++) {
  230.                     unsigned int size_x;
  231.                    
  232.                     size_x = conn->screenbuffer.size_x;
  233.                     interbuffer[i + j * size_x] =
  234.                         *get_field_at(&conn->screenbuffer, i, j);
  235.                 }
  236.             /* This call can preempt, but we are already at the end */
  237.             rc = async_req_0_0(fb_info.phone, FB_DRAW_TEXT_DATA);      
  238.         }
  239.        
  240.         if ((!interbuffer) || (rc != 0)) {
  241.             set_style(&conn->screenbuffer.style);
  242.             clrscr();
  243.             style = &conn->screenbuffer.style;
  244.            
  245.             for (j = 0; j < conn->screenbuffer.size_y; j++)
  246.                 for (i = 0; i < conn->screenbuffer.size_x; i++) {
  247.                     field = get_field_at(&conn->screenbuffer, i, j);
  248.                     if (!style_same(*style, field->style))
  249.                         set_style(&field->style);
  250.                     style = &field->style;
  251.                     if ((field->character == ' ') &&
  252.                         (style_same(field->style,
  253.                         conn->screenbuffer.style)))
  254.                         continue;
  255.                    
  256.                     prtchr(field->character, j, i);
  257.                 }
  258.         }
  259.        
  260.         curs_goto(conn->screenbuffer.position_y,
  261.             conn->screenbuffer.position_x);
  262.         curs_visibility(conn->screenbuffer.is_cursor_visible);
  263.        
  264.         async_serialize_end();
  265.     }
  266. }
  267.  
  268. /** Handler for keyboard */
  269. static void keyboard_events(ipc_callid_t iid, ipc_call_t *icall)
  270. {
  271.     ipc_callid_t callid;
  272.     ipc_call_t call;
  273.     int retval;
  274.     int c;
  275.     connection_t *conn;
  276.     int newcon;
  277.    
  278.     /* Ignore parameters, the connection is alread opened */
  279.     while (1) {
  280.         callid = async_get_call(&call);
  281.         switch (IPC_GET_METHOD(call)) {
  282.         case IPC_M_PHONE_HUNGUP:
  283.             /* TODO: Handle hangup */
  284.             return;
  285.         case KBD_MS_LEFT:
  286.             newcon = gcons_mouse_btn(IPC_GET_ARG1(call));
  287.             if (newcon != -1)
  288.                 change_console(newcon);
  289.             retval = 0;
  290.             break;
  291.         case KBD_MS_MOVE:
  292.             gcons_mouse_move(IPC_GET_ARG1(call),
  293.                 IPC_GET_ARG2(call));
  294.             retval = 0;
  295.             break;
  296.         case KBD_PUSHCHAR:
  297.             /* got key from keyboard driver */
  298.             retval = 0;
  299.             c = IPC_GET_ARG1(call);
  300.             /* switch to another virtual console */
  301.            
  302.             conn = &connections[active_console];
  303. /*
  304.  *          if ((c >= KBD_KEY_F1) && (c < KBD_KEY_F1 +
  305.  *              CONSOLE_COUNT)) {
  306.  */
  307.             if ((c >= 0x101) && (c < 0x101 + CONSOLE_COUNT)) {
  308.                 if (c == 0x112)
  309.                     change_console(KERNEL_CONSOLE);
  310.                 else
  311.                     change_console(c - 0x101);
  312.                 break;
  313.             }
  314.            
  315.             /* if client is awaiting key, send it */
  316.             if (conn->keyrequest_counter > 0) {    
  317.                 conn->keyrequest_counter--;
  318.                 ipc_answer_1(fifo_pop(conn->keyrequests), EOK,
  319.                     c);
  320.                 break;
  321.             }
  322.            
  323.             keybuffer_push(&conn->keybuffer, c);
  324.             retval = 0;
  325.            
  326.             break;
  327.         default:
  328.             retval = ENOENT;
  329.         }
  330.         ipc_answer_0(callid, retval);
  331.     }
  332. }
  333.  
  334. /** Default thread for new connections */
  335. static void client_connection(ipc_callid_t iid, ipc_call_t *icall)
  336. {
  337.     ipc_callid_t callid;
  338.     ipc_call_t call;
  339.     int consnum;
  340.     ipcarg_t arg1, arg2;
  341.     connection_t *conn;
  342.  
  343.     if ((consnum = find_free_connection()) == -1) {
  344.         ipc_answer_0(iid, ELIMIT);
  345.         return;
  346.     }
  347.     conn = &connections[consnum];
  348.     conn->used = 1;
  349.    
  350.     async_serialize_start();
  351.     gcons_notify_connect(consnum);
  352.     conn->client_phone = IPC_GET_ARG5(*icall);
  353.     screenbuffer_clear(&conn->screenbuffer);
  354.    
  355.     /* Accept the connection */
  356.     ipc_answer_0(iid, EOK);
  357.  
  358.     while (1) {
  359.         async_serialize_end();
  360.         callid = async_get_call(&call);
  361.         async_serialize_start();
  362.  
  363.         arg1 = 0;
  364.         arg2 = 0;
  365.         switch (IPC_GET_METHOD(call)) {
  366.         case IPC_M_PHONE_HUNGUP:
  367.             gcons_notify_disconnect(consnum);
  368.            
  369.             /* Answer all pending requests */
  370.             while (conn->keyrequest_counter > 0) {     
  371.                 conn->keyrequest_counter--;
  372.                 ipc_answer_0(fifo_pop(conn->keyrequests),
  373.                     ENOENT);
  374.                 break;
  375.             }
  376.             conn->used = 0;
  377.             return;
  378.         case CONSOLE_PUTCHAR:
  379.             write_char(consnum, IPC_GET_ARG1(call));
  380.             gcons_notify_char(consnum);
  381.             break;
  382.         case CONSOLE_CLEAR:
  383.             /* Send message to fb */
  384.             if (consnum == active_console) {
  385.                 async_msg_0(fb_info.phone, FB_CLEAR);
  386.             }
  387.            
  388.             screenbuffer_clear(&conn->screenbuffer);
  389.            
  390.             break;
  391.         case CONSOLE_GOTO:
  392.             screenbuffer_goto(&conn->screenbuffer,
  393.                 IPC_GET_ARG2(call), IPC_GET_ARG1(call));
  394.             if (consnum == active_console)
  395.                 curs_goto(IPC_GET_ARG1(call),
  396.                     IPC_GET_ARG2(call));
  397.             break;
  398.         case CONSOLE_GETSIZE:
  399.             arg1 = fb_info.rows;
  400.             arg2 = fb_info.cols;
  401.             break;
  402.         case CONSOLE_FLUSH:
  403.             if (consnum == active_console)
  404.                 async_req_0_0(fb_info.phone, FB_FLUSH);
  405.             break;
  406.         case CONSOLE_SET_STYLE:
  407.             arg1 = IPC_GET_ARG1(call);
  408.             arg2 = IPC_GET_ARG2(call);
  409.             screenbuffer_set_style(&conn->screenbuffer, arg1,
  410.                 arg2);
  411.             if (consnum == active_console)
  412.                 set_style_col(arg1, arg2);
  413.             break;
  414.         case CONSOLE_CURSOR_VISIBILITY:
  415.             arg1 = IPC_GET_ARG1(call);
  416.             conn->screenbuffer.is_cursor_visible = arg1;
  417.             if (consnum == active_console)
  418.                 curs_visibility(arg1);
  419.             break;
  420.         case CONSOLE_GETCHAR:
  421.             if (keybuffer_empty(&conn->keybuffer)) {
  422.                 /* buffer is empty -> store request */
  423.                 if (conn->keyrequest_counter <
  424.                     MAX_KEYREQUESTS_BUFFERED) {
  425.                     fifo_push(conn->keyrequests, callid);
  426.                     conn->keyrequest_counter++;
  427.                 } else {
  428.                     /*
  429.                      * No key available and too many
  430.                      * requests => fail.
  431.                     */
  432.                     ipc_answer_0(callid, ELIMIT);
  433.                 }
  434.                 continue;
  435.             }
  436.             int ch;
  437.             keybuffer_pop(&conn->keybuffer, &ch);
  438.             arg1 = ch;
  439.             break;
  440.         }
  441.         ipc_answer_2(callid, EOK, arg1, arg2);
  442.     }
  443. }
  444.  
  445. int main(int argc, char *argv[])
  446. {
  447.     printf(NAME ": HelenOS Console service\n");
  448.    
  449.     ipcarg_t phonehash;
  450.     int kbd_phone;
  451.     int i;
  452.  
  453.     async_set_client_connection(client_connection);
  454.    
  455.     /* Connect to keyboard driver */
  456.  
  457.     kbd_phone = ipc_connect_me_to(PHONE_NS, SERVICE_KEYBOARD, 0, 0);
  458.     while (kbd_phone < 0) {
  459.         usleep(10000);
  460.         kbd_phone = ipc_connect_me_to(PHONE_NS, SERVICE_KEYBOARD, 0, 0);
  461.     }
  462.    
  463.     if (ipc_connect_to_me(kbd_phone, SERVICE_CONSOLE, 0, 0, &phonehash) != 0)
  464.         return -1;
  465.     async_new_connection(phonehash, 0, NULL, keyboard_events);
  466.    
  467.     /* Connect to framebuffer driver */
  468.     fb_info.phone = ipc_connect_me_to(PHONE_NS, SERVICE_VIDEO, 0, 0);
  469.     while (fb_info.phone < 0) {
  470.         usleep(10000);
  471.         fb_info.phone = ipc_connect_me_to(PHONE_NS, SERVICE_VIDEO, 0, 0);
  472.     }
  473.    
  474.     /* Initialize gcons */
  475.     gcons_init(fb_info.phone);
  476.     /* Synchronize, the gcons can have something in queue */
  477.     async_req_0_0(fb_info.phone, FB_FLUSH);
  478.    
  479.     async_req_0_2(fb_info.phone, FB_GET_CSIZE, &fb_info.rows,
  480.         &fb_info.cols);
  481.     set_style_col(DEFAULT_FOREGROUND, DEFAULT_BACKGROUND);
  482.     clrscr();
  483.    
  484.     /* Init virtual consoles */
  485.     for (i = 0; i < CONSOLE_COUNT; i++) {
  486.         connections[i].used = 0;
  487.         keybuffer_init(&connections[i].keybuffer);
  488.        
  489.         connections[i].keyrequests.head = 0;
  490.         connections[i].keyrequests.tail = 0;
  491.         connections[i].keyrequests.items = MAX_KEYREQUESTS_BUFFERED;
  492.         connections[i].keyrequest_counter = 0;
  493.        
  494.         if (screenbuffer_init(&connections[i].screenbuffer,
  495.             fb_info.cols, fb_info.rows) == NULL) {
  496.             /* FIXME: handle error */
  497.             return -1;
  498.         }
  499.     }
  500.     connections[KERNEL_CONSOLE].used = 1;
  501.    
  502.     interbuffer = mmap(NULL,
  503.         sizeof(keyfield_t) * fb_info.cols * fb_info.rows,
  504.         PROTO_READ | PROTO_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
  505.     if (!interbuffer) {
  506.         if (ipc_share_out_start(fb_info.phone, interbuffer,
  507.             AS_AREA_READ) != EOK) {
  508.             munmap(interbuffer,
  509.                 sizeof(keyfield_t) * fb_info.cols * fb_info.rows);
  510.             interbuffer = NULL;
  511.         }
  512.     }
  513.  
  514.     curs_goto(0, 0);
  515.     curs_visibility(
  516.         connections[active_console].screenbuffer.is_cursor_visible);
  517.  
  518.     /* Register at NS */
  519.     if (ipc_connect_to_me(PHONE_NS, SERVICE_CONSOLE, 0, 0, &phonehash) != 0)
  520.         return -1;
  521.    
  522.     // FIXME: avoid connectiong to itself, keep using klog
  523.     // printf(NAME ": Accepting connections\n");
  524.     async_manager();
  525.  
  526.     return 0;  
  527. }
  528.  
  529. /** @}
  530.  */
  531.