/* 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; vcons_t vcons; vcons_t new_vcons; /* 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. So we acquire a reference for VCONS to make sure it isn't deallocated while we are outside of the global lock. We also know that */ mutex_lock (&global_lock); vcons = active_vcons; if (vcons) ports_port_ref (vcons); mutex_unlock (&global_lock); err = cons_switch (vcons, id, delta, &new_vcons); if (!err) { mutex_lock (&global_lock); if (active_vcons != new_vcons) { cons_vcons_close (active_vcons); active_vcons = new_vcons; } mutex_unlock (&new_vcons->lock); ports_port_deref (vcons); 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); } void cons_vcons_set_scroll_lock (vcons_t vcons, int onoff) { } 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; }