Author: bdonlan
Date: 2004-12-15 15:08:55 -0500 (Wed, 15 Dec 2004)
New Revision: 413

Added:
   trunk/clients/ncurses/
   trunk/clients/ncurses/display.c
   trunk/clients/ncurses/display.h
   trunk/clients/ncurses/entry.c
   trunk/clients/ncurses/entry.h
   trunk/clients/ncurses/event.c
   trunk/clients/ncurses/event.h
   trunk/clients/ncurses/main.c
   trunk/clients/ncurses/mymalloc.c
   trunk/clients/ncurses/mymalloc.h
Log:
Imported beginnings of a C/ncurses client. Currently this is mostly a test
of the event/IO systems, and the command line.



Property changes on: trunk/clients/ncurses
___________________________________________________________________
Name: svn:ignore
   + ?      configure
?      Makefile.in
?      config.log
?      depcomp
?      config.guess
?      config.h
?      config.sub
?      ltmain.sh
?      .libs
?      .deps
?      Makefile
?      mkinstalldirs
?      config.status
?      stamp-h1
?      config.h.in
?      havercurs
?      autom4te.cache
?      libtool
?      missing
?      aclocal.m4
?      install-sh


Added: trunk/clients/ncurses/display.c
===================================================================
--- trunk/clients/ncurses/display.c     2004-12-15 04:35:17 UTC (rev 412)
+++ trunk/clients/ncurses/display.c     2004-12-15 20:08:55 UTC (rev 413)
@@ -0,0 +1,23 @@
+#include "display.h"
+#include <ncurses.h>
+
+static WINDOW *dwin = NULL;
+
+void display_init(int lines) {
+       dwin = newwin(lines, COLS, 0, 0);
+       idlok(dwin, TRUE);
+       scrollok(dwin, TRUE);
+}
+
+void display_free(void) {
+       delwin(dwin);
+       dwin = NULL;
+}
+
+void display_print(const char *fmt, ...) {
+       va_list ap;
+       va_start(ap, fmt);
+       vw_printw(dwin, fmt, ap);
+       va_end(ap);
+       wnoutrefresh(dwin);
+}


Property changes on: trunk/clients/ncurses/display.c
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/clients/ncurses/display.h
===================================================================
--- trunk/clients/ncurses/display.h     2004-12-15 04:35:17 UTC (rev 412)
+++ trunk/clients/ncurses/display.h     2004-12-15 20:08:55 UTC (rev 413)
@@ -0,0 +1,9 @@
+#ifndef DISPLAY_H
+#define DISPLAY_H 1
+
+void display_init(int lines);
+void display_free(void);
+
+void display_print(const char *fmt, ...);
+
+#endif


Property changes on: trunk/clients/ncurses/display.h
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/clients/ncurses/entry.c
===================================================================
--- trunk/clients/ncurses/entry.c       2004-12-15 04:35:17 UTC (rev 412)
+++ trunk/clients/ncurses/entry.c       2004-12-15 20:08:55 UTC (rev 413)
@@ -0,0 +1,108 @@
+#include "mymalloc.h"
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <ncurses.h>
+
+static char *buffer = NULL;
+int pos, len, blen, win;
+
+void entry_init(void) {
+       pos = len = win = 0;
+       blen = 32;
+       buffer = mymalloc(blen);
+}
+
+void entry_free(void) {
+       free(buffer);
+       buffer = NULL;
+}
+
+int entry_get_pos(void) {
+       return pos;
+}
+
+int entry_get_len(void) {
+       return len;
+}
+
+static void checkpos(void) {
+       if (pos < 0)
+               pos = 0;
+       if (pos > len)
+               pos = len;
+}
+
+char entry_remove_ch(int offset) {
+       char c;
+       if (offset >= len || offset < 0)
+               return 0;
+       c = buffer[offset];
+       memmove(&buffer[offset], &buffer[offset + 1], len - offset - 1);
+       len--;
+       checkpos();
+       return c;
+}
+
+static void expand_buf(void) {
+       blen = (blen * 3) / 2;
+       buffer = myrealloc(buffer, blen);
+}
+
+void entry_insert_ch(int offset, char c) {
+       if (offset < 0 || offset > len) {
+               assert(0);
+               abort();
+       }
+       if (len + 1 > blen)
+               expand_buf();
+       memmove(&buffer[offset + 1], &buffer[offset], len - offset);
+       buffer[offset] = c;
+       len++;
+}
+
+int entry_move_abs(int offset) {
+       pos = offset;
+       checkpos();
+       return pos;
+}
+
+int entry_move_rel(int offset) {
+       pos += offset;
+       checkpos();
+       return pos;
+}
+
+const char *entry_get(void) {
+       if (len + 1 > blen)
+               expand_buf();
+       buffer[len] = '\0'; /* null-terminate it for the caller */
+       return buffer;
+}
+
+void entry_clear(void) {
+       len = pos = win = 0;
+}
+
+void entry_refresh(WINDOW *w, int yoff, int cols) {
+       int i, lim;
+       wmove(w, yoff, 0);
+       wclrtoeol(w);
+
+       /* Is the window sane? */
+       if (pos < win ||
+               pos > win + cols
+          )
+       {
+               win = pos - (cols / 2);
+               if (win < 0)
+                       win = 0;
+       }
+
+       lim = win + cols;
+       for (i = win; i < lim && i < len; i++) {
+               addch(buffer[i]);
+       }
+       wmove(w, yoff, pos - win);
+       wnoutrefresh(w);
+}


Property changes on: trunk/clients/ncurses/entry.c
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/clients/ncurses/entry.h
===================================================================
--- trunk/clients/ncurses/entry.h       2004-12-15 04:35:17 UTC (rev 412)
+++ trunk/clients/ncurses/entry.h       2004-12-15 20:08:55 UTC (rev 413)
@@ -0,0 +1,21 @@
+#ifndef ENTRY_H
+#define ENTRY_H 1
+
+void entry_init(void);
+void entry_free(void);
+
+int entry_get_pos(void);
+int entry_get_len(void);
+
+char entry_remove_ch(int offset);
+void entry_insert_ch(int offset, char c);
+
+int entry_move_abs(int offset);
+int entry_move_rel(int offset);
+
+const char *entry_get(void);
+void entry_clear(void);
+
+void entry_refresh(WINDOW *w, int yoff, int cols);
+
+#endif


Property changes on: trunk/clients/ncurses/entry.h
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/clients/ncurses/event.c
===================================================================
--- trunk/clients/ncurses/event.c       2004-12-15 04:35:17 UTC (rev 412)
+++ trunk/clients/ncurses/event.c       2004-12-15 20:08:55 UTC (rev 413)
@@ -0,0 +1,265 @@
+#include "mymalloc.h"
+#include "event.h"
+#include <sys/time.h>
+#include <sys/select.h>
+
+typedef struct event_timer_struct {
+       struct event_timer_struct *next;
+       struct timeval when;
+       event_timer_callback *cb;
+       void *baton;
+} event_timer_t;
+
+typedef struct event_fd_struct {
+       struct event_fd_struct *next;
+       int fd;
+       int mode;
+       event_fd_callback *cb;
+       void *baton;
+} event_fd_t;  
+
+static event_timer_t *t_head = NULL;
+static event_fd_t *fd_head = NULL;
+
+static fd_set cache_r, cache_w, cache_e;
+static int fd_max = 0;
+
+void event_init(void) {
+       FD_ZERO(&cache_r);
+       FD_ZERO(&cache_w);
+       FD_ZERO(&cache_e);
+}
+
+void event_free(void) {
+       event_timer_t *t_cur = t_head;
+       event_fd_t *io_cur = fd_head;
+       while (t_cur) {
+               event_timer_t *next = t_cur;
+               free(t_cur);
+               t_cur = next;
+       }
+       t_head = NULL;
+       while (io_cur) {
+               event_fd_t *next = io_cur;
+               free(io_cur);
+               io_cur = next;
+       }
+       fd_head = NULL; 
+}
+
+void event_timer_abs(
+               const struct timeval *when,
+               event_timer_callback *cb,
+               void *baton
+               ) {
+       event_timer_t *new = mymalloc(sizeof *new);
+       event_timer_t *cur, *prev;
+       new->when = *when;
+       new->cb = cb;
+       new->baton = baton;
+       prev = NULL;
+       cur = t_head;
+       while (cur) {
+               if (cur->when.tv_sec < new->when.tv_sec ||
+                       cur->when.tv_usec < new->when.tv_usec
+                  )
+               {
+                       break;
+               }
+               cur = cur->next;
+       }
+       if (prev)
+               prev->next = new;
+       else
+               t_head = new;
+       new->next = cur;                
+}
+
+void event_timer_rel(
+               const struct timeval *when,
+               event_timer_callback *cb,
+               void *baton
+               )
+{
+       struct timeval absolute;
+       gettimeofday(&absolute, NULL);
+       absolute.tv_sec += when->tv_sec;
+       absolute.tv_usec += when->tv_usec;
+       if (absolute.tv_usec > 1000000) {
+               absolute.tv_sec += absolute.tv_usec / 1000000;
+               absolute.tv_usec %= 1000000;
+       }
+       event_timer_abs(&absolute, cb, baton);
+}
+
+static int check_timers(
+               const struct timeval *now,
+               const struct timeval **next
+               )
+{
+       int counter = 0;
+       const struct timeval *dummy;
+       if (!next)
+               next = &dummy;
+       while (t_head) {
+               if (t_head->when.tv_sec < now->tv_sec ||
+                               (t_head->when.tv_sec == now->tv_sec &&
+                                t_head->when.tv_usec < now->tv_usec
+                               ))
+               {
+                       event_timer_t *cur = t_head;
+                       counter++;
+                       t_head = cur->next;
+                       cur->cb(&cur->when, cur->baton);
+                       free(cur);
+               } else break;
+       }
+       if (t_head)
+               *next = &t_head->when;
+       else
+               *next = NULL;
+       return counter;
+}
+
+static void recache_fd(event_fd_t *what, int add) {
+       if (what->mode & EVENT_FD_READABLE)
+               FD_SET(what->fd, &cache_r);
+       else
+               FD_CLR(what->fd, &cache_r);
+       if (what->mode & EVENT_FD_WRITABLE)
+               FD_SET(what->fd, &cache_w);
+       else
+               FD_CLR(what->fd, &cache_w);
+       if (what->mode & EVENT_FD_ERROR)
+               FD_SET(what->fd, &cache_e);
+       else
+               FD_CLR(what->fd, &cache_e);
+
+       if (add) {
+               if (what->fd + 1 > fd_max)
+                       fd_max = what->fd + 1;
+       } else {
+               if (what->fd + 1 == fd_max) {
+                       event_fd_t *cur = fd_head;
+                       fd_max = 0;
+                       while (cur) {
+                               if (cur->fd + 1 > fd_max)
+                                       fd_max = cur->fd + 1;
+                               cur = cur->next;
+                       }
+               }
+       }
+}
+
+static event_fd_t *search(int fd, event_fd_t **prev) {
+       event_fd_t *dummy, *cur;
+       if (!prev)
+               prev = &dummy;
+       cur = fd_head;
+       while (cur) {
+               if (cur->fd == fd)
+                       return cur;
+               *prev = cur;
+               cur = cur->next;
+       }
+       return NULL;
+}
+
+void event_addfd(
+               int fd,
+               int mode,
+               event_fd_callback *cb,
+               void *baton
+               ) {
+       static event_fd_t *prev, *cur;
+       cur = search(fd, &prev);
+       if (!cur) {
+               cur = mymalloc(sizeof *cur);
+               cur->fd = fd;
+               if (prev) {
+                       cur->next = prev->next;
+                       prev->next = cur;
+               } else {
+                       cur->next = fd_head;
+                       fd_head = cur;
+               }
+       }
+       cur->cb = cb;
+       cur->mode = mode;
+       cur->baton = baton;
+       recache_fd(cur, 1);
+}
+
+void event_delfd(int fd) {
+       static event_fd_t *prev, *cur;
+       cur = search(fd, &prev);
+       if (!cur)
+               return;
+       if (prev)
+               prev->next = cur->next;
+       else
+               fd_head = cur->next;
+       recache_fd(cur, 0);
+}
+
+static int io_poll(struct timeval *timeout) {
+       fd_set r, w, e;
+       int count = 0;
+       int remain = 0;
+       event_fd_t *cur = fd_head;
+       r = cache_r;
+       w = cache_w;
+       e = cache_e;
+       remain = select(fd_max, &r, &w, &e, timeout);
+       while (remain > 0 && cur) {
+               int mode = 0;
+               int fd = cur->fd;
+               if (FD_ISSET(fd, &r)) {
+                       remain--;
+                       mode |= EVENT_FD_READABLE;
+               }
+               if (FD_ISSET(fd, &w)) {
+                       remain--;
+                       mode |= EVENT_FD_WRITABLE;
+               }
+               if (FD_ISSET(fd, &e)) {
+                       remain--;
+                       mode |= EVENT_FD_ERROR;
+               }
+               if (mode & ~cur->mode)
+                       recache_fd(cur, 1);
+               mode &= cur->mode;
+               if (mode) {
+                       count++;
+                       cur->cb(fd, mode, cur->baton);
+               }
+               cur = cur->next;
+       }
+       return count;
+}
+
+int event_poll(int block) {
+       int counter = 0;
+       struct timeval now;
+       const struct timeval *next;
+       struct timeval tv, *p_tv = NULL;
+       gettimeofday(&now, NULL);
+       counter += check_timers(&now, &next);
+       if (block && next) {
+               tv.tv_sec = next->tv_sec - now.tv_sec;
+               tv.tv_usec = next->tv_usec - now.tv_usec;
+               if (tv.tv_usec < 0) {
+                       tv.tv_sec--;
+                       tv.tv_usec += 1000000;
+               }
+               p_tv = &tv;
+       }
+       if (!block) {
+               tv.tv_sec = tv.tv_usec = 0;
+               p_tv = &tv;
+       }
+       counter += io_poll(p_tv);
+       gettimeofday(&now, NULL);
+       counter += check_timers(&now, NULL);
+       return counter;
+}


Property changes on: trunk/clients/ncurses/event.c
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/clients/ncurses/event.h
===================================================================
--- trunk/clients/ncurses/event.h       2004-12-15 04:35:17 UTC (rev 412)
+++ trunk/clients/ncurses/event.h       2004-12-15 20:08:55 UTC (rev 413)
@@ -0,0 +1,37 @@
+#ifndef EVENT_H
+#define EVENT_H 1
+
+#define EVENT_FD_READABLE      1
+#define EVENT_FD_WRITABLE      2
+#define EVENT_FD_ERROR         4
+
+typedef void event_fd_callback(int fd, int mode, void *baton);
+typedef void event_timer_callback(const struct timeval *when, void *baton);
+
+void event_init(void);
+void event_free(void);
+
+void event_timer_abs(
+               const struct timeval *when,
+               event_timer_callback *cb,
+               void *baton
+               );
+
+void event_timer_rel(
+               const struct timeval *when,
+               event_timer_callback *cb,
+               void *baton
+               );
+
+void event_addfd(
+               int fd,
+               int mode,
+               event_fd_callback *cb,
+               void *baton
+               );
+
+void event_delfd(int fd);
+
+int event_poll(int block);
+
+#endif


Property changes on: trunk/clients/ncurses/event.h
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/clients/ncurses/main.c
===================================================================
--- trunk/clients/ncurses/main.c        2004-12-15 04:35:17 UTC (rev 412)
+++ trunk/clients/ncurses/main.c        2004-12-15 20:08:55 UTC (rev 413)
@@ -0,0 +1,116 @@
+#include <ncurses.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "mymalloc.h"
+#include "entry.h"
+#include "display.h"
+/*#include "io.h"*/
+#include "event.h"
+
+void finish(int);
+void handle_input(int fd, int mode, void *baton);
+void heartbeat(const struct timeval *when, void *baton);
+
+int main(void) {
+       int i;
+       signal(SIGINT, finish);      /* arrange interrupts to terminate */
+       initscr();
+       nonl();
+       cbreak();
+       noecho();
+       idlok(stdscr, TRUE);
+       keypad(stdscr, TRUE);
+
+       move(LINES - 2, 0);
+       for (i = 0; i < COLS; i++)
+               addch('-');
+       move(LINES - 1, 0);
+       
+       entry_init();
+/*     io_init();*/
+       event_init();
+       display_init(LINES - 2);
+
+       heartbeat(NULL, "Heartbeat 1");
+
+       refresh();
+
+       event_addfd(0, EVENT_FD_READABLE | EVENT_FD_ERROR, handle_input, NULL);
+       
+       for (;;) {
+               int c = event_poll(1);
+               entry_refresh(stdscr, LINES - 1, COLS);
+               refresh();
+       }
+}
+
+void finish(int sig) {
+       endwin();
+       exit(0);
+}
+
+void handle_input(int fd, int mode, void *baton) {
+       int c;
+       size_t pos, len;
+       
+       mode = mode & (EVENT_FD_READABLE | EVENT_FD_ERROR);
+       if (!mode)
+               return; /* We're willing to block on stdout. */
+       
+       if (mode & EVENT_FD_ERROR) {
+               endwin();
+               exit(0);
+       }
+       pos = entry_get_pos();
+       len = entry_get_len();
+       
+       switch (c = getch()) {
+               case KEY_UP:
+                       break;
+               case KEY_DOWN:
+                       break;
+               case KEY_BACKSPACE:
+                       if (pos > 0)
+                               entry_remove_ch(pos - 1);
+                       entry_move_abs(pos - 1);
+                       break;
+               case KEY_DC:
+                       if (len && pos < len)
+                               entry_remove_ch(pos);
+                       entry_move_abs(pos);
+                       break;
+               case KEY_ENTER:
+               case '\n':
+               case '\r':
+                       display_print("%s\n",entry_get());
+                       /* fallthru */
+               case KEY_DL:
+                       entry_clear();
+                       break;
+               case KEY_LEFT:
+                       entry_move_rel(-1);
+                       break;
+               case KEY_RIGHT:
+                       entry_move_rel(1);
+                       break;
+               default:
+                       if (!isprint(c)) {
+                               display_print("%d\n", (int)c);  
+                               break;
+                       }
+                       entry_insert_ch(pos, c);
+                       entry_move_rel(1);
+       };
+}
+
+void heartbeat(const struct timeval *when, void *baton) {
+       const char *message = baton;
+       struct timeval next;
+       display_print("%s\n", message);
+       next.tv_sec = 1;
+       next.tv_usec = 0;
+       event_timer_rel(&next, heartbeat, baton);
+}


Property changes on: trunk/clients/ncurses/main.c
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/clients/ncurses/mymalloc.c
===================================================================
--- trunk/clients/ncurses/mymalloc.c    2004-12-15 04:35:17 UTC (rev 412)
+++ trunk/clients/ncurses/mymalloc.c    2004-12-15 20:08:55 UTC (rev 413)
@@ -0,0 +1,21 @@
+#include <stdlib.h>
+#include <ncurses.h>
+#include "mymalloc.h"
+
+void *mymalloc(size_t s) {
+       void *p = malloc(s);
+       if (!p) {
+               endwin();
+               abort();
+       }
+       return p;
+}
+
+void *myrealloc(void *oldp, size_t s) {
+       void *p = realloc(oldp, s);
+       if (!p) {
+               endwin();
+               abort();
+       }
+       return p;
+}


Property changes on: trunk/clients/ncurses/mymalloc.c
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/clients/ncurses/mymalloc.h
===================================================================
--- trunk/clients/ncurses/mymalloc.h    2004-12-15 04:35:17 UTC (rev 412)
+++ trunk/clients/ncurses/mymalloc.h    2004-12-15 20:08:55 UTC (rev 413)
@@ -0,0 +1,9 @@
+#ifndef MYMALLOC_H
+#define MYMALLOC_H 1
+
+#include <stdlib.h>
+
+void *mymalloc(size_t s);
+void *myrealloc(void *oldp, size_t s);
+
+#endif


Property changes on: trunk/clients/ncurses/mymalloc.h
___________________________________________________________________
Name: svn:eol-style
   + native


Reply via email to