Hi, here is my current version of the ncurses console client. If you don't want to do some debugging with the current console server it will be pretty useless, though. Of course, the important work has been done in the console server, but as this is the client to go with those changes, I post it here.
The code is probably worse than the last version. The reason is that this is now using shared memory and file notification messages for the communication, and the details of this are quite hairy. It turns out that the communication protocol has some hidden quirks. Nothing that is unsolvable, and nothing that is really unexpected, but it leads to some messy code (like the clipping calculation that checks if a changed part of the file is currently displayed). Actually, most of the problems are directly the result of using a ring buffer. I don't want to give up on the ring buffer, as it allows for very efficient scrolling and scrollback buffers on the server side, and in general is a very good idea. But with a ring buffer, you always have the boundary case that a region spans over the end of the physical buffer. (If you want an exercise, rewrite display.c:screen_shift_left() without using a loop, but by calculating the boundaries and using wmemmove, wmemcpy and wmemset. It's mind boggling). To solve this at least partially, I will make some hard assumptions about the communication protocol between the server and client. I will abuse the notification interface a bit. In particular with EXTEND and TRUNCATE, which will always come in pairs, and signal scrolling rather than file size changes, and the order in which they appear actually matters (if you first truncate, then extend, you are scrolling towards the front of the file, if you first extend, then truncate, you are scrolling to the right. This distinction is necessary to make [start:end] with start>end disambiguously referring to linear regions spanning across the physical end of the ring buffer). But luckily, I can hide all this mess in a libconsole-client, and let all clients share the code and not bother about the protocol details like this one. But this library is not written yet. Which is why this program is so messy. And of course, the protocol is not finished yet at all. For now, my main goal is to make it functional, which involves a few short cuts here and there. It will be made nicer a bit later. If you tried the last version, you will notice that this one has more bugs: scrolling doesn't work correctly (there is a grave clipping bug, and the scrolling notifications are not implemented), and probably other things don't work well either. But internally it has improved because all the server/client infrastructure is there now (almost, I still need to enable seqnos in the client and process the notification messages in order). Compile with something like this (ourfs_notify.defs from console/ in the Hurd sources): mig -n -server ourfs_notifyServer.c ourfs_notify.defs CFLAGS=-Wall -g -I. -D_GNU_SOURCE console-client-curses: console-client-curses.o ourfs_notifyServer.o $(CC) console-client-curses.o ourfs_notifyServer.o -lncursesw -lports -lthreads -o console-client-curses Thanks, Marcus -- `Rhubarb is no Egyptian god.' Debian http://www.debian.org [EMAIL PROTECTED] Marcus Brinkmann GNU http://www.gnu.org [EMAIL PROTECTED] [EMAIL PROTECTED] http://www.marcus-brinkmann.de /* console-client-curses.c -- A console client based on curses. 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 <errno.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <wchar.h> #include <error.h> #include <fcntl.h> #include <assert.h> #include <sys/mman.h> #include <ncursesw/curses.h> #include <cthreads.h> #include <hurd/hurd_types.h> #include <hurd/ports.h> #include "console.h" struct mutex ncurses_lock; any_t input_loop (any_t fdp) { int fd = 0; int wfd = *(int *) fdp; fd_set rfds; 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) { switch (ret) { case KEY_DOWN: /* etc */ /* Do something */ break; default: buf[size++] = ret; assert (size != 100); break; } } mutex_unlock (&ncurses_lock); do { ret = write (wfd, buf, size); if (ret > 0) { size -= ret; buf += ret; } } while (ret != -1 || errno == EINTR); } } } void mvwputsn (wchar_t *str, size_t len, int x, int y) { cchar_t chr; wchar_t wch[2] = { L'\0', L'\0' }; move (y, x); while (len) { int ret; wch[0] = *str; ret = setcchar (&chr, wch, 0, 0, NULL); /* if (ret == ERR) { printf ("setcchar failed: %s\n", strerror (errno)); printf ("[%lc]\n", wch[0]); assert (!"Do something if setcchar fails."); }*/ ret = add_wch (&chr); /* 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."); }*/ len--; str++; } } static struct cons_display *disp; static mach_port_t disp_notify; kern_return_t dir_changed (mach_port_t notify_port, dir_changed_type_t change, string_t name) { return EOPNOTSUPP; } kern_return_t file_changed (mach_port_t notify_port, file_changed_type_t change, off_t start, off_t end) { static struct cons_display _cdisp; static struct cons_display *cdisp = &_cdisp; if (notify_port != disp_notify) return EOPNOTSUPP; switch (change) { case FILE_CHANGED_NULL: /* Always sent first for sync. */ mutex_lock (&ncurses_lock); *cdisp = *disp; mvwputsn (disp->_matrix + cdisp->screen.cur_line * cdisp->screen.width, ((cdisp->screen.lines - cdisp->screen.cur_line < cdisp->screen.height) ? cdisp->screen.lines - cdisp->screen.cur_line : cdisp->screen.height) * cdisp->screen.width, 0, 0); if (cdisp->screen.lines - cdisp->screen.cur_line < cdisp->screen.height) mvwputsn (cdisp->_matrix, 2000 - (cdisp->screen.lines - cdisp->screen.cur_line) * cdisp->screen.width, 0, cdisp->screen.lines - cdisp->screen.cur_line); move (cdisp->cursor.row, cdisp->cursor.col); refresh (); mutex_unlock (&ncurses_lock); break; case FILE_CHANGED_WRITE: /* File data has been written. */ if (start == offsetof (struct cons_display, cursor.col)) { int status_changed = 0; mutex_lock (&ncurses_lock); cdisp->cursor.col = disp->cursor.col; cdisp->cursor.row = disp->cursor.row; if (end <= offsetof (struct cons_display, cursor.status)) cdisp->cursor.status = disp->cursor.status; move (cdisp->cursor.row, cdisp->cursor.col); if (status_changed) curs_set (cdisp->cursor.status ? (cdisp->cursor.status == 1 ? 1 : 2) : 0); refresh (); mutex_unlock (&ncurses_lock); } else if (start == offsetof (struct cons_display, screen.cur_line)) { uint32_t new_cur_line; int scrolling; mutex_lock (&ncurses_lock); new_cur_line = disp->screen.cur_line; if (end <= offsetof (struct cons_display, screen.scr_lines)) cdisp->screen.scr_lines = disp->screen.scr_lines; scrolling = new_cur_line - cdisp->screen.cur_line; if (scrolling < 0) scrolling += cdisp->screen.lines; idlok (stdscr, TRUE); scrollok (stdscr, TRUE); scrl (scrolling); refresh (); idlok (stdscr, FALSE); scrollok (stdscr, FALSE); cdisp->screen.cur_line = new_cur_line; mutex_unlock (&ncurses_lock); } else if (start >= offsetof (struct cons_display, _matrix)) { /* For clipping. */ off_t size = cdisp->screen.width * cdisp->screen.lines; off_t vis_start = cdisp->screen.width * cdisp->screen.cur_line; off_t vis_end = (cdisp->screen.cur_line + cdisp->screen.height) * cdisp->screen.width - 1; start -= sizeof (struct cons_display); start /= sizeof (wchar_t); end -= sizeof (struct cons_display); end /= sizeof (wchar_t); if (start > end) end += cdisp->screen.width * cdisp->screen.lines; if (start < vis_start) start = vis_start; if (end > vis_end) end = vis_end; if (start <= end) { mutex_lock (&ncurses_lock); mvwputsn (disp->_matrix + start, end < size ? end - start + 1 : size - start, (start - vis_start) % cdisp->screen.width, (start - vis_start) / cdisp->screen.width); if (end >= size) mvwputsn (disp->_matrix, end - size + 1, (size - vis_start) % cdisp->screen.width, (size - vis_start) / cdisp->screen.width); /* XXX */ move (cdisp->cursor.row, cdisp->cursor.col); refresh (); mutex_unlock (&ncurses_lock); } } break; case FILE_CHANGED_EXTEND: /* File has grown. */ // break; case FILE_CHANGED_TRUNCATE: /* File has been truncated. */ // break; case FILE_CHANGED_META: /* Stat information has changed, and none of the previous three apply. Not sent for changes in node times. */ default: return EINVAL; }; return 0; } extern int fs_notify_server (mach_msg_header_t *inp, mach_msg_header_t *outp); int main (int argc, char *argv[]) { error_t err; char *display_name; char *input_name; int dispfd; mach_port_t disp_port; int inptfd; struct stat statbuf; int ret; struct port_class *notify_class; struct port_bucket *notify_bucket; struct port_info *notify_port; mutex_init (&ncurses_lock); initscr (); cbreak (); noecho (); nonl (); intrflush (stdscr, FALSE); // timeout (1000); nodelay (stdscr, TRUE); keypad (stdscr, TRUE); if (argc != 2) error (1, 0, "missing vc dir name"); asprintf (&display_name, "%s/display", argv[1]); asprintf (&input_name, "%s/input", argv[1]); dispfd = open (display_name, O_RDONLY); if (dispfd < 0) error (2, errno, "opening %s failed", display_name); ret = fstat (dispfd, &statbuf); if (ret < 0) error (3, errno, "stat'ing %s failed", display_name); disp = mmap (0, statbuf.st_size, PROT_READ, MAP_SHARED, dispfd, 0); if (disp == MAP_FAILED) error (3, errno, "mmap failed"); inptfd = open (input_name, O_WRONLY); if (inptfd < 0) error (2, errno, "opening %s failed", input_name); cthread_detach (cthread_fork (input_loop, &inptfd)); notify_class = ports_create_class (NULL /* clean */, NULL /* dropweak */); if (! notify_class) error (5, errno, "Cannot create notify class"); notify_bucket = ports_create_bucket (); if (! notify_bucket) error (5, errno, "Cannot create notify bucket"); err = ports_create_port (notify_class, notify_bucket, 0, ¬ify_port); if (err) error (5, errno, "Cannot create notify port"); disp_notify = ports_get_right (notify_port); disp_port = getdport (dispfd); if (disp_port == MACH_PORT_NULL) error (5, errno, "Cannot get display file port"); err = file_notice_changes (disp_port, disp_notify, MACH_MSG_TYPE_MAKE_SEND); if (err) error (5, errno, "Cannot request notification messages"); ports_manage_port_operations_multithread (notify_bucket, fs_notify_server, 1000 * 60 * 2, 1000 * 60 * 10, 0); endwin (); return 0; } _______________________________________________ Bug-hurd mailing list [EMAIL PROTECTED] http://mail.gnu.org/mailman/listinfo/bug-hurd