/* console-ncurses.c -- A console client based on ncurses. Copyright (C) 2002 Free Software Foundation, Inc. Written by Marcus Brinkmann. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include /* The makefiles make sure that this program is compiled with -I${prefix}/ncursesw. */ #include #include #include #include #include struct mutex ncurses_lock; const char *cons_client_name = "console-ncurses"; const char *cons_client_version = HURD_VERSION; struct curses_kc_to_cons_kc { int curses; char *cons; }; struct curses_kc_to_cons_kc keycodes[] = { { KEY_BREAK, NULL }, /* XXX */ { KEY_DOWN, CONS_KEY_DOWN }, { KEY_UP, CONS_KEY_UP }, { KEY_RIGHT, CONS_KEY_RIGHT }, { KEY_LEFT, CONS_KEY_LEFT }, { KEY_HOME, CONS_KEY_HOME }, { KEY_BACKSPACE, CONS_KEY_BACKSPACE }, { KEY_F(1), CONS_KEY_F1 }, { KEY_F(2), CONS_KEY_F2 }, { KEY_F(3), CONS_KEY_F3 }, { KEY_F(4), CONS_KEY_F4 }, { KEY_F(5), CONS_KEY_F5 }, { KEY_F(6), CONS_KEY_F6 }, { KEY_F(7), CONS_KEY_F7 }, { KEY_F(8), CONS_KEY_F8 }, { KEY_F(9), CONS_KEY_F9 }, { KEY_F(10), CONS_KEY_F10 }, { KEY_DL, NULL }, /* XXX Delete line. */ { KEY_IL, NULL }, /* XXX Insert line. */ { KEY_DC, CONS_KEY_DC }, { KEY_IC, CONS_KEY_IC }, { KEY_EIC, NULL }, /* XXX Exit insert mode. */ { KEY_CLEAR, NULL }, /* XXX Clear screen. */ { KEY_EOS, NULL }, /* XXX Clear to end of screen. */ { KEY_EOL, NULL }, /* XXX Clear to end of line. */ { KEY_SF, NULL }, /* XXX Scroll one line forward. */ { KEY_SR, NULL }, /* XXX Scroll one line backward. */ { KEY_NPAGE, CONS_KEY_NPAGE }, { KEY_PPAGE, CONS_KEY_PPAGE }, { KEY_STAB, NULL }, /* XXX Set tab. */ { KEY_CTAB, NULL }, /* XXX Clear tab. */ { KEY_CATAB, NULL }, /* XXX Clear all tabs. */ { KEY_ENTER, NULL }, /* XXX Enter or send. */ { KEY_SRESET, NULL }, /* XXX Soft (partial) reset. */ { KEY_RESET, NULL }, /* XXX Reset or hard reset. */ { KEY_PRINT, NULL }, /* XXX Print or copy. */ { KEY_LL, NULL }, /* XXX Home down or bottom (lower left). */ { KEY_A1, NULL }, /* XXX Upper left of keypad. */ { KEY_A3, NULL }, /* XXX Upper right of keypad. */ { KEY_B2, NULL }, /* XXX Center of keypad. */ { KEY_C1, NULL }, /* XXX Lower left of keypad. */ { KEY_C3, NULL }, /* XXX Lower right of keypad. */ { KEY_BTAB, CONS_KEY_BTAB }, { KEY_BEG, NULL }, /* XXX Beg(inning) key. */ { KEY_CANCEL, NULL }, /* XXX Cancel key. */ { KEY_CLOSE, NULL }, /* XXX Close key. */ { KEY_COMMAND, NULL }, /* XXX Cmd (command) key. */ { KEY_COPY, NULL }, /* XXX Copy key. */ { KEY_CREATE, NULL }, /* XXX Create key. */ { KEY_END, CONS_KEY_END }, { KEY_EXIT, NULL }, /* XXX Exit key. */ { KEY_FIND, NULL }, /* XXX Find key. */ { KEY_HELP, NULL }, /* XXX Help key. */ { KEY_MARK, NULL }, /* XXX Mark key. */ { KEY_MESSAGE, NULL }, /* XXX Message key. */ { KEY_MOUSE, NULL }, /* XXX Mouse event read. */ { KEY_MOVE, NULL }, /* XXX Move key. */ { KEY_NEXT, NULL }, /* XXX Next object key. */ { KEY_OPEN, NULL }, /* XXX Open key. */ { KEY_OPTIONS, NULL }, /* XXX Options key. */ { KEY_PREVIOUS, NULL }, /* XXX Previous object key. */ { KEY_REDO, NULL }, /* XXX Redo key. */ { KEY_REFERENCE, NULL }, /* XXX Ref(erence) key. */ { KEY_REFRESH, NULL }, /* XXX Refresh key. */ { KEY_REPLACE, NULL }, /* XXX Replace key. */ { KEY_RESIZE, NULL }, /* XXX Screen resized. */ { KEY_RESTART, NULL }, /* XXX Restart key. */ { KEY_RESUME, NULL }, /* XXX Resume key. */ { KEY_SAVE, NULL }, /* XXX Save key. */ { KEY_SBEG, NULL }, /* XXX Shifted beginning key. */ { KEY_SCANCEL, NULL }, /* XXX Shifted cancel key. */ { KEY_SCOMMAND, NULL }, /* XXX Shifted command key. */ { KEY_SCOPY, NULL }, /* XXX Shifted copy key. */ { KEY_SCREATE, NULL }, /* XXX Shifted create key. */ { KEY_SDC, NULL }, /* XXX Shifted delete char key. */ { KEY_SDL, NULL }, /* XXX Shifted delete line key. */ { KEY_SELECT, NULL }, /* XXX Select key. */ { KEY_SEND, NULL }, /* XXX Shifted end key. */ { KEY_SEOL, NULL }, /* XXX Shifted clear line key. */ { KEY_SEXIT, NULL }, /* XXX Shifted exit key. */ { KEY_SFIND, NULL }, /* XXX Shifted find key. */ { KEY_SHELP, NULL }, /* XXX Shifted help key. */ { KEY_SHOME, NULL }, /* XXX Shifted home key. */ { KEY_SIC, NULL }, /* XXX Shifted input key. */ { KEY_SLEFT, NULL }, /* XXX Shifted left arrow key. */ { KEY_SMESSAGE, NULL }, /* XXX Shifted message key. */ { KEY_SMOVE, NULL }, /* XXX Shifted move key. */ { KEY_SNEXT, NULL }, /* XXX Shifted next key. */ { KEY_SOPTIONS, NULL }, /* XXX Shifted options key. */ { KEY_SPREVIOUS, NULL }, /* XXX Shifted prev key. */ { KEY_SPRINT, NULL }, /* XXX Shifted print key. */ { KEY_SREDO, NULL }, /* XXX Shifted redo key. */ { KEY_SREPLACE, NULL }, /* XXX Shifted replace key. */ { KEY_SRIGHT, NULL }, /* XXX Shifted right arrow. */ { KEY_SRSUME, NULL }, /* XXX Shifted resume key. */ { KEY_SSAVE, NULL }, /* XXX Shifted save key. */ { KEY_SSUSPEND, NULL }, /* XXX Shifted suspend key. */ { KEY_SUNDO, NULL }, /* XXX Shifted undo key. */ { KEY_SUSPEND, NULL }, /* XXX Suspend key. */ { KEY_UNDO, NULL } /* XXX Undo key. */ }; int ucs4_to_altchar (wchar_t chr, chtype *achr) { switch (chr) { case CONS_CHAR_RARROW: *achr = ACS_RARROW; break; case CONS_CHAR_LARROW: *achr = ACS_LARROW; break; case CONS_CHAR_UARROW: *achr = ACS_UARROW; break; case CONS_CHAR_DARROW: *achr = ACS_DARROW; break; case CONS_CHAR_BLOCK: *achr = ACS_BLOCK; break; case CONS_CHAR_LANTERN: *achr = ACS_LANTERN; break; case CONS_CHAR_DIAMOND: *achr = ACS_DIAMOND; break; case CONS_CHAR_CKBOARD: *achr = ACS_CKBOARD; break; case CONS_CHAR_DEGREE: *achr = ACS_DEGREE; break; case CONS_CHAR_PLMINUS: *achr = ACS_PLMINUS; break; case CONS_CHAR_BOARD: *achr = ACS_BOARD; break; case CONS_CHAR_LRCORNER: *achr = ACS_LRCORNER; break; case CONS_CHAR_URCORNER: *achr = ACS_URCORNER; break; case CONS_CHAR_ULCORNER: *achr = ACS_ULCORNER; break; case CONS_CHAR_LLCORNER: *achr = ACS_LLCORNER; break; case CONS_CHAR_PLUS: *achr = ACS_PLUS; break; case CONS_CHAR_S1: *achr = ACS_S1; break; case CONS_CHAR_S3: *achr = ACS_S3; break; case CONS_CHAR_HLINE: *achr = ACS_HLINE; break; case CONS_CHAR_S7: *achr = ACS_S7; break; case CONS_CHAR_S9: *achr = ACS_S9; break; case CONS_CHAR_LTEE: *achr = ACS_LTEE; break; case CONS_CHAR_RTEE: *achr = ACS_RTEE; break; case CONS_CHAR_BTEE: *achr = ACS_BTEE; break; case CONS_CHAR_TTEE: *achr = ACS_TTEE; break; case CONS_CHAR_VLINE: *achr = ACS_VLINE; break; case CONS_CHAR_LEQUAL: *achr = ACS_LEQUAL; break; case CONS_CHAR_GEQUAL: *achr = ACS_GEQUAL; break; case CONS_CHAR_PI: *achr = ACS_PI; break; case CONS_CHAR_NEQUAL: *achr = ACS_NEQUAL; break; case CONS_CHAR_STERLING: *achr = ACS_STERLING; break; case CONS_CHAR_BULLET: *achr = ACS_BULLET; break; default: return 0; } return 1; } struct mutex global_lock; static vcons_t active_vcons = NULL; error_t console_switch (int id, int delta) { error_t err = 0; int active_id; cons_t cons; vcons_t vcons; mutex_lock (&global_lock); if (!active_vcons) { mutex_unlock (&global_lock); return 0; } cons = active_vcons->cons; active_id = active_vcons->id; /* We must give up our global lock before we can call back into libcons. This is because cons_switch will lock CONS, and as other functions in libcons lock CONS while calling back into our functions which take the global lock (like cons_vcons_add), we would deadlock. Because of this, we can not refer to our active console directly, but we must refer to it by name (ID). XXX Likewise, we can not really refer to CONS directly, but the current implementation guarantees an eternal life span for CONS. */ mutex_unlock (&global_lock); err = cons_switch (cons, active_id, id, delta, &vcons); if (!err) { mutex_lock (&global_lock); if (active_vcons != vcons) { cons_vcons_close (active_vcons); active_vcons = vcons; } mutex_unlock (&vcons->lock); mutex_unlock (&global_lock); } return err; } void cons_vcons_add (cons_t cons, vcons_list_t vcons_entry) { error_t err = 0; mutex_lock (&global_lock); if (!active_vcons) { vcons_t vcons; err = cons_vcons_open (cons, vcons_entry, &vcons); if (!err) { vcons_entry->vcons = vcons; active_vcons = vcons; mutex_unlock (&vcons->lock); } } mutex_unlock (&global_lock); } any_t input_loop (any_t unused) { int fd = 0; fd_set rfds; int w_escaped = 0; FD_ZERO (&rfds); FD_SET (fd, &rfds); while (1) { int ret; FD_SET (fd, &rfds); ret = select (fd + 1, &rfds, 0, 0, 0); if (ret == 1) { char buffer[100]; char *buf = buffer; size_t size = 0; mutex_lock (&ncurses_lock); while ((ret = getch ()) != ERR) { int i; int found; if (w_escaped) { switch (ret) { case 'x': endwin (); exit (0); break; case 23: /* ^W */ assert (size < 100); buf[size++] = ret; break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* Avoid a dead lock. */ mutex_unlock (&ncurses_lock); console_switch (1 + (ret - '1'), 0); mutex_lock (&ncurses_lock); break; default: break; } w_escaped = 0; } else switch (ret) { case 23: /* ^W */ w_escaped = 1; break; default: found = 0; for (i =0; i < sizeof(keycodes) / sizeof(keycodes[0]); i++) { if (keycodes[i].curses == ret) { if (keycodes[i].cons) { assert (size < 101 - strlen(keycodes[i].cons)); strcpy (&buf[size], keycodes[i].cons); size += strlen (keycodes[i].cons); } found = 1; break; } } if (!found) { assert (size < 100); buf[size++] = ret; } break; } } mutex_unlock (&ncurses_lock); if (size) { mutex_lock (&global_lock); if (active_vcons) { do { ret = write (active_vcons->input, buf, size); if (ret > 0) { size -= ret; buf += ret; } } while (size && (ret != -1 || errno == EINTR)); } mutex_unlock (&global_lock); } } } } inline attr_t conchar_attr_to_attr (conchar_attr_t attr) { return ((attr.intensity == CONS_ATTR_INTENSITY_BOLD ? A_BOLD : (attr.intensity == CONS_ATTR_INTENSITY_DIM ? A_DIM : A_NORMAL)) | (attr.underlined ? A_UNDERLINE : 0) | (attr.reversed ? A_REVERSE : 0) | (attr.blinking ? A_BLINK: 0) | (attr.concealed ? A_INVIS : 0)); } inline short conchar_attr_to_color_pair (conchar_attr_t attr) { return COLOR_PAIR (attr.bgcol << 3 | attr.fgcol); } void mvwputsn (conchar_t *str, size_t len, off_t x, off_t y) { cchar_t chr; wchar_t wch[2] = { L'\0', L'\0' }; uint32_t last_attr = * (uint32_t *) &str->attr; attr_t attr = conchar_attr_to_attr (str->attr); short color_pair = conchar_attr_to_color_pair (str->attr); move (y, x); while (len) { int ret; chtype ac; if (last_attr != *(uint32_t *) &str->attr) { last_attr = * (uint32_t *) &str->attr; attr = conchar_attr_to_attr (str->attr); color_pair = conchar_attr_to_color_pair (str->attr); } if (ucs4_to_altchar (str->chr, &ac)) addch (ac | attr | color_pair); else { wch[0] = str->chr; ret = setcchar (&chr, wch, attr, color_pair, NULL); #if 0 if (ret == ERR) { printf ("setcchar failed: %s\n", strerror (errno)); printf ("[%lc]\n", wch[0]); assert (!"Do something if setcchar fails."); } #endif ret = add_wch (&chr); #if 0 if (ret == ERR) { printf ("add_wch failed: %i, %s\n", ret, strerror (errno)); printf ("[%lc]\n", wch[0]); assert (!"Do something if add_wchr fails."); } #endif } len--; str++; } } void cons_vcons_update (vcons_t vcons) { mutex_lock (&global_lock); if (vcons == active_vcons) refresh (); mutex_unlock (&global_lock); } void cons_vcons_set_cursor_pos (vcons_t vcons, uint32_t col, uint32_t row) { mutex_lock (&global_lock); if (vcons == active_vcons) { mutex_lock (&ncurses_lock); move (row, col); mutex_unlock (&ncurses_lock); } mutex_unlock (&global_lock); } void cons_vcons_set_cursor_status (vcons_t vcons, uint32_t status) { mutex_lock (&global_lock); if (vcons == active_vcons) { mutex_lock (&ncurses_lock); curs_set (status ? (status == 1 ? 1 : 2) : 0); mutex_unlock (&ncurses_lock); } mutex_unlock (&global_lock); } void cons_vcons_scroll (vcons_t vcons, int delta) { assert (delta >= 0); mutex_lock (&global_lock); if (vcons == active_vcons) { mutex_lock (&ncurses_lock); idlok (stdscr, TRUE); scrollok (stdscr, TRUE); scrl (delta); idlok (stdscr, FALSE); scrollok (stdscr, FALSE); mutex_unlock (&ncurses_lock); } mutex_unlock (&global_lock); } void cons_vcons_write (vcons_t vcons, conchar_t *str, size_t length, uint32_t col, uint32_t row) { int x; int y; mutex_lock (&global_lock); if (vcons == active_vcons) { mutex_lock (&ncurses_lock); getyx (stdscr, y, x); mvwputsn (str, length, col, row); wmove (stdscr, y, x); mutex_unlock (&ncurses_lock); } mutex_unlock (&global_lock); } void cons_vcons_beep (vcons_t vcons) { mutex_lock (&global_lock); if (vcons == active_vcons) { mutex_lock (&ncurses_lock); beep (); mutex_unlock (&ncurses_lock); } mutex_unlock (&global_lock); } void cons_vcons_flash (vcons_t vcons) { mutex_lock (&global_lock); if (vcons == active_vcons) { mutex_lock (&ncurses_lock); flash (); mutex_unlock (&ncurses_lock); } mutex_unlock (&global_lock); } int main (int argc, char *argv[]) { error_t err; int i; /* Parse our command line. This shouldn't ever return an error. */ argp_parse (&cons_startup_argp, argc, argv, 0, 0, 0); mutex_init (&ncurses_lock); mutex_init (&global_lock); initscr (); start_color (); for (i = 0; i < 64; i++) init_pair (i, i & 7, i >> 3); raw (); noecho (); nonl (); intrflush (stdscr, FALSE); nodelay (stdscr, TRUE); timeout (1); keypad (stdscr, TRUE); cthread_detach (cthread_fork (input_loop, NULL)); err = cons_init (); if (err) { endwin (); error (5, err, "Console library initialization failed"); } cons_server_loop (); /* Never reached. */ endwin (); return 0; }