console.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2006 Josef Cejka
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  */
00028 
00035 /* TODO: remove */
00036 #include <stdio.h>
00037 
00038 #include <fb.h>
00039 #include <ipc/ipc.h>
00040 #include <keys.h>
00041 #include <ipc/fb.h>
00042 #include <ipc/services.h>
00043 #include <errno.h>
00044 #include <key_buffer.h>
00045 #include <console.h>
00046 #include <unistd.h>
00047 #include <async.h>
00048 #include <libadt/fifo.h>
00049 #include <screenbuffer.h>
00050 #include <sys/mman.h>
00051 
00052 #include "gcons.h"
00053 
00054 #define MAX_KEYREQUESTS_BUFFERED 32
00055 
00056 #define NAME "CONSOLE"
00057 
00060 int active_console = 0;
00061 
00064 struct {
00065         int phone;              
00066         ipcarg_t rows;          
00067         ipcarg_t cols;          
00068 } fb_info;
00069 
00070 
00071 typedef struct {
00072         keybuffer_t keybuffer;          
00073         FIFO_CREATE_STATIC(keyrequests, ipc_callid_t , MAX_KEYREQUESTS_BUFFERED);       
00074         int keyrequest_counter;         
00075         int client_phone;               
00076         int used;                       
00077         screenbuffer_t screenbuffer;    
00078 } connection_t;
00079 
00080 static connection_t connections[CONSOLE_COUNT]; 
00081 static keyfield_t *interbuffer = NULL;                  
00083 static int kernel_pixmap = -1;      
00089 static int find_free_connection(void) 
00090 {
00091         int i = 0;
00092         
00093         for (i=0; i < CONSOLE_COUNT; i++) {
00094                 if (!connections[i].used)
00095                         return i;
00096         }
00097         return -1;
00098 }
00099 
00100 static void clrscr(void)
00101 {
00102         async_msg(fb_info.phone, FB_CLEAR, 0);
00103 }
00104 
00105 static void curs_visibility(int v)
00106 {
00107         async_msg(fb_info.phone, FB_CURSOR_VISIBILITY, v); 
00108 }
00109 
00110 static void curs_goto(int row, int col)
00111 {
00112         async_msg_2(fb_info.phone, FB_CURSOR_GOTO, row, col); 
00113         
00114 }
00115 
00116 static void set_style(style_t *style)
00117 {
00118         async_msg_2(fb_info.phone, FB_SET_STYLE, style->fg_color, style->bg_color); 
00119 }
00120 
00121 static void set_style_col(int fgcolor, int bgcolor)
00122 {
00123         async_msg_2(fb_info.phone, FB_SET_STYLE, fgcolor, bgcolor); 
00124 }
00125 
00126 static void prtchr(char c, int row, int col)
00127 {
00128         async_msg_3(fb_info.phone, FB_PUTCHAR, c, row, col);
00129         
00130 }
00131 
00135 static void write_char(int console, char key)
00136 {
00137         screenbuffer_t *scr = &(connections[console].screenbuffer);
00138         
00139         switch (key) {
00140                 case '\n':
00141                         scr->position_y += 1;
00142                         scr->position_x =  0;
00143                         break;
00144                 case '\r':
00145                         break;
00146                 case '\t':
00147                         scr->position_x += 8;
00148                         scr->position_x -= scr->position_x % 8; 
00149                         break;
00150                 case '\b':
00151                         if (scr->position_x == 0) 
00152                                 break;
00153 
00154                         scr->position_x--;
00155 
00156                         if (console == active_console)
00157                                 prtchr(' ', scr->position_y, scr->position_x);
00158         
00159                         screenbuffer_putchar(scr, ' ');
00160                         
00161                         break;
00162                 default:        
00163                         if (console == active_console)
00164                                 prtchr(key, scr->position_y, scr->position_x);
00165         
00166                         screenbuffer_putchar(scr, key);
00167                         scr->position_x++;
00168         }
00169         
00170         scr->position_y += (scr->position_x >= scr->size_x);
00171         
00172         if (scr->position_y >= scr->size_y) {
00173                 scr->position_y = scr->size_y - 1;
00174                 screenbuffer_clear_line(scr, scr->top_line);
00175                 scr->top_line = (scr->top_line+1) % scr->size_y;
00176                 if (console == active_console)
00177                         async_msg(fb_info.phone, FB_SCROLL, 1);
00178         }
00179         
00180         scr->position_x = scr->position_x % scr->size_x;
00181         
00182         if (console == active_console)
00183                 curs_goto(scr->position_y, scr->position_x);
00184         
00185 }
00186 
00192 static int switch_screens(int oldpixmap)
00193 {
00194         int newpmap;
00195        
00196         /* Save screen */
00197         newpmap = async_req(fb_info.phone, FB_VP2PIXMAP, 0, NULL);
00198         if (newpmap < 0)
00199                 return -1;
00200 
00201         if (oldpixmap != -1) {
00202                 /* Show old screen */
00203                 async_msg_2(fb_info.phone, FB_VP_DRAW_PIXMAP, 0, oldpixmap);
00204                 /* Drop old pixmap */
00205                 async_msg(fb_info.phone, FB_DROP_PIXMAP, oldpixmap);
00206         }
00207         
00208         return newpmap;
00209 }
00210 
00212 static void change_console(int newcons)
00213 {
00214         connection_t *conn;
00215         static int console_pixmap = -1;
00216         int i, j, rc;
00217         keyfield_t *field;
00218         style_t *style;
00219 
00220         if (newcons == active_console)
00221                 return;
00222 
00223         if (newcons == KERNEL_CONSOLE) {
00224                 if (active_console == KERNEL_CONSOLE)
00225                         return;
00226                 active_console = KERNEL_CONSOLE;
00227                 curs_visibility(0);
00228 
00229                 async_serialize_start();
00230                 if (kernel_pixmap == -1) { 
00231                         /* store/restore unsupported */
00232                         set_style_col(DEFAULT_FOREGROUND, DEFAULT_BACKGROUND);
00233                         clrscr();
00234                 } else {
00235                         gcons_in_kernel();
00236                         console_pixmap = switch_screens(kernel_pixmap);
00237                         kernel_pixmap = -1;
00238                 }
00239                 async_serialize_end();
00240 
00241                 __SYSCALL0(SYS_DEBUG_ENABLE_CONSOLE);
00242                 return;
00243         } 
00244         
00245         async_serialize_start();
00246 
00247         if (console_pixmap != -1) {
00248                 kernel_pixmap = switch_screens(console_pixmap);
00249                 console_pixmap = -1;
00250         }
00251         active_console = newcons;
00252         gcons_change_console(newcons);
00253         conn = &connections[active_console];
00254 
00255         set_style(&conn->screenbuffer.style);
00256         curs_visibility(0);
00257         if (interbuffer) {
00258                 for (i = 0; i < conn->screenbuffer.size_x; i++)
00259                         for (j = 0; j < conn->screenbuffer.size_y; j++) 
00260                                 interbuffer[i + j*conn->screenbuffer.size_x] = *get_field_at(&(conn->screenbuffer),i, j);
00261                 /* This call can preempt, but we are already at the end */
00262                 rc = async_req_2(fb_info.phone, FB_DRAW_TEXT_DATA, 0, 0, NULL, NULL);           
00263         };
00264         
00265         if ((!interbuffer) || (rc != 0)) {
00266                 set_style(&conn->screenbuffer.style);
00267                 clrscr();
00268                 style = &conn->screenbuffer.style;
00269 
00270                 for (j = 0; j < conn->screenbuffer.size_y; j++) 
00271                         for (i = 0; i < conn->screenbuffer.size_x; i++) {
00272                                 field = get_field_at(&(conn->screenbuffer),i, j);
00273                                 if (!style_same(*style, field->style))
00274                                         set_style(&field->style);
00275                                 style = &field->style;
00276                                 if ((field->character == ' ') && (style_same(field->style, conn->screenbuffer.style)))
00277                                         continue;
00278 
00279                                 prtchr(field->character, j, i);
00280                         }
00281         }
00282         
00283         curs_goto(conn->screenbuffer.position_y, conn->screenbuffer.position_x);
00284         curs_visibility(conn->screenbuffer.is_cursor_visible);
00285 
00286         async_serialize_end();
00287 }
00288 
00290 static void keyboard_events(ipc_callid_t iid, ipc_call_t *icall)
00291 {
00292         ipc_callid_t callid;
00293         ipc_call_t call;
00294         int retval;
00295         int c;
00296         connection_t *conn;
00297         int newcon;
00298         
00299         /* Ignore parameters, the connection is alread opened */
00300         while (1) {
00301                 callid = async_get_call(&call);
00302                 switch (IPC_GET_METHOD(call)) {
00303                 case IPC_M_PHONE_HUNGUP:
00304                         /* TODO: Handle hangup */
00305                         return;
00306                 case KBD_MS_LEFT:
00307                         newcon = gcons_mouse_btn(IPC_GET_ARG1(call));
00308                         if (newcon != -1)
00309                                 change_console(newcon);
00310                         retval = 0;
00311                         break;
00312                 case KBD_MS_MOVE:
00313                         gcons_mouse_move(IPC_GET_ARG1(call), IPC_GET_ARG2(call));
00314                         retval = 0;
00315                         break;
00316                 case KBD_PUSHCHAR:
00317                         /* got key from keyboard driver */
00318                         
00319                         retval = 0;
00320                         c = IPC_GET_ARG1(call);
00321                         /* switch to another virtual console */
00322                         
00323                         conn = &connections[active_console];
00324 //                      if ((c >= KBD_KEY_F1) && (c < KBD_KEY_F1 + CONSOLE_COUNT)) {
00325                         if ((c >= 0x101) && (c < 0x101 + CONSOLE_COUNT)) {
00326                                 if (c == 0x112)
00327                                         change_console(KERNEL_CONSOLE);
00328                                 else
00329                                         change_console(c - 0x101);
00330                                 break;
00331                         }
00332                         
00333                         /* if client is awaiting key, send it */
00334                         if (conn->keyrequest_counter > 0) {             
00335                                 conn->keyrequest_counter--;
00336                                 ipc_answer_fast(fifo_pop(conn->keyrequests), 0, c, 0);
00337                                 break;
00338                         }
00339                         
00340                         keybuffer_push(&conn->keybuffer, c);
00341                         retval = 0;
00342                         
00343                         break;
00344                 default:
00345                         retval = ENOENT;
00346                 }
00347                 ipc_answer_fast(callid, retval, 0, 0);
00348         }
00349 }
00350 
00352 static void client_connection(ipc_callid_t iid, ipc_call_t *icall)
00353 {
00354         ipc_callid_t callid;
00355         ipc_call_t call;
00356         int consnum;
00357         ipcarg_t arg1, arg2;
00358         connection_t *conn;
00359 
00360         if ((consnum = find_free_connection()) == -1) {
00361                 ipc_answer_fast(iid,ELIMIT,0,0);
00362                 return;
00363         }
00364         conn = &connections[consnum];
00365         conn->used = 1;
00366         
00367         async_serialize_start();
00368         gcons_notify_connect(consnum);
00369         conn->client_phone = IPC_GET_ARG3(call);
00370         screenbuffer_clear(&conn->screenbuffer);
00371         
00372         /* Accept the connection */
00373         ipc_answer_fast(iid,0,0,0);
00374 
00375         while (1) {
00376                 async_serialize_end();
00377                 callid = async_get_call(&call);
00378                 async_serialize_start();
00379 
00380                 arg1 = arg2 = 0;
00381                 switch (IPC_GET_METHOD(call)) {
00382                 case IPC_M_PHONE_HUNGUP:
00383                         gcons_notify_disconnect(consnum);
00384                         
00385                         /* Answer all pending requests */
00386                         while (conn->keyrequest_counter > 0) {          
00387                                 conn->keyrequest_counter--;
00388                                 ipc_answer_fast(fifo_pop(conn->keyrequests), ENOENT, 0, 0);
00389                                 break;
00390                         }
00391                         conn->used = 0;
00392                         return;
00393                 case CONSOLE_PUTCHAR:
00394                         write_char(consnum, IPC_GET_ARG1(call));
00395                         gcons_notify_char(consnum);
00396                         break;
00397                 case CONSOLE_CLEAR:
00398                         /* Send message to fb */
00399                         if (consnum == active_console) {
00400                                 async_msg(fb_info.phone, FB_CLEAR, 0); 
00401                         }
00402                         
00403                         screenbuffer_clear(&conn->screenbuffer);
00404                         
00405                         break;
00406                 case CONSOLE_GOTO:
00407                         
00408                         screenbuffer_goto(&conn->screenbuffer, IPC_GET_ARG2(call), IPC_GET_ARG1(call));
00409                         if (consnum == active_console)
00410                                 curs_goto(IPC_GET_ARG1(call),IPC_GET_ARG2(call));
00411                         
00412                         break;
00413 
00414                 case CONSOLE_GETSIZE:
00415                         arg1 = fb_info.rows;
00416                         arg2 = fb_info.cols;
00417                         break;
00418                 case CONSOLE_FLUSH:
00419                         if (consnum == active_console)
00420                                 async_req_2(fb_info.phone, FB_FLUSH, 0, 0, NULL, NULL);         
00421                         break;
00422                 case CONSOLE_SET_STYLE:
00423                         
00424                         arg1 = IPC_GET_ARG1(call);
00425                         arg2 = IPC_GET_ARG2(call);
00426                         screenbuffer_set_style(&conn->screenbuffer,arg1, arg2);
00427                         if (consnum == active_console)
00428                                 set_style_col(arg1, arg2);
00429                                 
00430                         break;
00431                 case CONSOLE_CURSOR_VISIBILITY:
00432                         arg1 = IPC_GET_ARG1(call);
00433                         conn->screenbuffer.is_cursor_visible = arg1;
00434                         if (consnum == active_console)
00435                                 curs_visibility(arg1);
00436                         break;
00437                 case CONSOLE_GETCHAR:
00438                         if (keybuffer_empty(&conn->keybuffer)) {
00439                                 /* buffer is empty -> store request */
00440                                 if (conn->keyrequest_counter < MAX_KEYREQUESTS_BUFFERED) {              
00441                                         fifo_push(conn->keyrequests, callid);
00442                                         conn->keyrequest_counter++;
00443                                 } else {
00444                                         /* no key available and too many requests => fail */
00445                                         ipc_answer_fast(callid, ELIMIT, 0, 0);
00446                                 }
00447                                 continue;
00448                         };
00449                         keybuffer_pop(&conn->keybuffer, (int *)&arg1);
00450                         
00451                         break;
00452                 }
00453                 ipc_answer_fast(callid, 0, arg1, arg2);
00454         }
00455 }
00456 
00457 int main(int argc, char *argv[])
00458 {
00459         ipcarg_t phonehash;
00460         int kbd_phone;
00461         int i;
00462 
00463         async_set_client_connection(client_connection);
00464         
00465         /* Connect to keyboard driver */
00466 
00467         while ((kbd_phone = ipc_connect_me_to(PHONE_NS, SERVICE_KEYBOARD, 0)) < 0) {
00468                 usleep(10000);
00469         };
00470         
00471         if (ipc_connect_to_me(kbd_phone, SERVICE_CONSOLE, 0, &phonehash) != 0) {
00472                 return -1;
00473         };
00474         async_new_connection(phonehash, 0, NULL, keyboard_events);
00475         
00476         /* Connect to framebuffer driver */
00477         
00478         while ((fb_info.phone = ipc_connect_me_to(PHONE_NS, SERVICE_VIDEO, 0)) < 0) {
00479                 usleep(10000);
00480         }
00481         
00482         /* Save old kernel screen */
00483         kernel_pixmap = switch_screens(-1);
00484 
00485         /* Initialize gcons */
00486         gcons_init(fb_info.phone);
00487         /* Synchronize, the gcons can have something in queue */
00488         async_req(fb_info.phone, FB_FLUSH, 0, NULL);
00489         /* Enable double buffering */
00490         async_msg_2(fb_info.phone, FB_VIEWPORT_DB, (sysarg_t)-1, 1);
00491         
00492         async_req_2(fb_info.phone, FB_GET_CSIZE, 0, 0, &(fb_info.rows), &(fb_info.cols)); 
00493         set_style_col(DEFAULT_FOREGROUND, DEFAULT_BACKGROUND);
00494         clrscr();
00495         
00496         /* Init virtual consoles */
00497         for (i = 0; i < CONSOLE_COUNT; i++) {
00498                 connections[i].used = 0;
00499                 keybuffer_init(&(connections[i].keybuffer));
00500                 
00501                 connections[i].keyrequests.head = connections[i].keyrequests.tail = 0;
00502                 connections[i].keyrequests.items = MAX_KEYREQUESTS_BUFFERED;
00503                 connections[i].keyrequest_counter = 0;
00504                 
00505                 if (screenbuffer_init(&(connections[i].screenbuffer), fb_info.cols, fb_info.rows ) == NULL) {
00506                         /*FIXME: handle error */
00507                         return -1;
00508                 }
00509         }
00510         connections[KERNEL_CONSOLE].used = 1;
00511         
00512         if ((interbuffer = mmap(NULL, sizeof(keyfield_t) * fb_info.cols * fb_info.rows , PROTO_READ|PROTO_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, 0 ,0 )) != NULL) {
00513                 if (async_req_3(fb_info.phone, IPC_M_AS_AREA_SEND, (ipcarg_t)interbuffer, 0, AS_AREA_READ, NULL, NULL, NULL) != 0) {
00514                         munmap(interbuffer, sizeof(keyfield_t) * fb_info.cols * fb_info.rows);
00515                         interbuffer = NULL;
00516                 }
00517         }
00518 
00519         curs_goto(0,0);
00520         curs_visibility(connections[active_console].screenbuffer.is_cursor_visible);
00521 
00522         /* Register at NS */
00523         if (ipc_connect_to_me(PHONE_NS, SERVICE_CONSOLE, 0, &phonehash) != 0) {
00524                 return -1;
00525         };
00526         
00527         async_manager();
00528 
00529         return 0;       
00530 }
00531  

Generated on Sun Jun 18 17:54:20 2006 for HelenOS Userspace (ia32) by  doxygen 1.4.6