chiark / gitweb /
*** empty log message ***
[sympathy.git] / apps / mainloop.c
index 5acee6fc6840f1e51affc3101671d8de4d9b837e..146ebdd5a7f8229ea0b69836abc2f0f644e73bfd 100644 (file)
  *
  */
 
-static char rcsid[] = "$Id$";
+static char rcsid[] =
+  "$Id$";
 
 /*
  * $Log$
- * Revision 1.1  2008/02/16 01:30:56  james
+ * Revision 1.16  2008/02/27 09:42:21  james
+ * *** empty log message ***
+ *
+ * Revision 1.15  2008/02/27 01:31:38  james
+ * *** empty log message ***
+ *
+ * Revision 1.14  2008/02/27 01:31:14  james
+ * *** empty log message ***
+ *
+ * Revision 1.13  2008/02/26 23:56:12  james
+ * *** empty log message ***
+ *
+ * Revision 1.12  2008/02/26 23:23:16  james
+ * *** empty log message ***
+ *
+ * Revision 1.11  2008/02/24 00:43:55  james
+ * *** empty log message ***
+ *
+ * Revision 1.10  2008/02/23 13:05:57  staffcvs
+ * *** empty log message ***
+ *
+ * Revision 1.9  2008/02/23 11:48:51  james
+ * *** empty log message ***
+ *
+ * Revision 1.8  2008/02/22 23:39:30  james
+ * *** empty log message ***
+ *
+ * Revision 1.7  2008/02/20 20:16:07  james
+ * *** empty log message ***
+ *
+ * Revision 1.6  2008/02/20 19:44:37  james
+ * @@
+ *
+ * Revision 1.5  2008/02/20 18:31:44  james
+ * *** empty log message ***
+ *
+ * Revision 1.4  2008/02/20 17:18:33  james
+ * *** empty log message ***
+ *
+ * Revision 1.3  2008/02/20 02:11:35  james
+ * *** empty log message ***
+ *
+ * Revision 1.2  2008/02/16 10:58:52  james
+ * *** empty log message ***
+ *
+ * Revision 1.13  2008/02/15 23:52:12  james
+ * *** empty log message ***
+ *
+ * Revision 1.12  2008/02/15 03:32:07  james
+ * *** empty log message ***
+ *
+ * Revision 1.11  2008/02/14 16:21:17  james
+ * *** empty log message ***
+ *
+ * Revision 1.10  2008/02/14 10:39:14  james
+ * *** empty log message ***
+ *
+ * Revision 1.9  2008/02/14 10:34:47  james
+ * *** empty log message ***
+ *
+ * Revision 1.8  2008/02/14 10:34:30  james
+ * *** empty log message ***
+ *
+ * Revision 1.7  2008/02/14 02:46:44  james
+ * *** empty log message ***
+ *
+ * Revision 1.6  2008/02/14 00:57:58  james
+ * *** empty log message ***
+ *
+ * Revision 1.5  2008/02/13 18:05:06  james
+ * *** empty log message ***
+ *
+ * Revision 1.4  2008/02/13 17:21:55  james
+ * *** empty log message ***
+ *
+ * Revision 1.3  2008/02/08 15:06:52  james
+ * *** empty log message ***
+ *
+ * Revision 1.2  2008/02/07 15:42:49  james
+ * *** empty log message ***
+ *
+ * Revision 1.1  2008/02/05 14:25:49  james
  * *** empty log message ***
  *
  */
 
+#include <sys/time.h>
+#include <sympathy.h>
+#include "mainloop.h"
+
+#include "clients.h"
+
+Context *context;
+
+typedef struct
+{
+  int nclients;
+  int lines;
+  int baud;
+  int crtscts;
+  int cd_edge_sec;
+  int blocked;
+  int bootstrap;
+} Status;
+
+static Status
+get_status (TTY * t, Clients * cs)
+{
+  static struct timeval last_cd_edge = { 0 };
+  static int last_cd_state = -1;
+  int cd;
+  struct timeval now, dif;
+
+  TTY_Status tty_status = { 0 };
+  Status status;
+
+  tty_get_status (t, &tty_status);
+
+  status.bootstrap = 1;
+
+  if (cs)
+    status.nclients = cs->n;
+  else
+    status.nclients = 0;
+
+  status.lines = tty_status.lines;
+  status.baud = tty_status.baud;
+  status.crtscts = (tty_status.termios.c_cflag & CRTSCTS) ? 1 : 0;
+  status.blocked = tty_status.blocked;
+
+  cd = (tty_status.lines & TIOCM_CD) ? 1 : 0;
+
+  if (cd != last_cd_state)
+    {
+      gettimeofday (&last_cd_edge, NULL);
+      last_cd_state = cd;
+    }
+
+  gettimeofday (&now, NULL);
+  timersub (&now, &last_cd_edge, &dif);
+  status.cd_edge_sec = dif.tv_sec;
+
+  return status;
+}
+
+static char *
+line_to_name (int l)
+{
+
+  switch (l)
+    {
+#ifdef TIOCM_LE
+    case TIOCM_LE:
+      return "LE";
+#endif
+#ifdef TIOCM_DTR
+    case TIOCM_DTR:
+      return "DTR";
+#endif
+#ifdef TIOCM_RTS
+    case TIOCM_RTS:
+      return "RTS";
+#endif
+#ifdef TIOCM_ST
+    case TIOCM_ST:
+      return "ST";
+#endif
+#ifdef TIOCM_SR
+    case TIOCM_SR:
+      return "SR";
+#endif
+#ifdef TIOCM_CTS
+    case TIOCM_CTS:
+      return "CTS";
+#endif
+#ifdef TIOCM_CD
+    case TIOCM_CD:
+      return "CD";
+#endif
+#ifdef TIOCM_RI
+    case TIOCM_RI:
+      return "RI";
+#endif
+#ifdef TIOCM_DSR
+    case TIOCM_DSR:
+      return "DSR";
+#endif
+    }
+  return "??";
+}
+
+static void
+log_line_changes (Context * ctx, int old, int new)
+{
+  int dif = old ^ new;
+  int c = 1;
+  char buf[1024], *ptr = buf;
+  char *n;
+
+  if (!dif)
+    return;
+  if (!ctx->l)
+    return;
+
+  n = "<Modem lines changed:";
+
+  while (*n)
+    *(ptr++) = *(n++);
+
+  while (dif >= c)
+    {
+
+      if (dif & c)
+        {
+          *(ptr++) = ' ';
+          *(ptr++) = (new & c) ? '+' : '-';
+          n = line_to_name (c);
+          while (*n)
+            *(ptr++) = *(n++);
+        }
+
+      c <<= 1;
+    }
+  *(ptr++) = '>';
+  *ptr = 0;
+
+
+  ctx->l->log (ctx->l, buf);
+
+}
+
+static char *
+do_line (char *ptr, int lines, int line)
+{
+  char *lname;
+
+  if (!(lines & line))
+    return ptr;
+  lname = line_to_name (line);
+
+  *(ptr++) = ' ';
+  while (*lname)
+    *(ptr++) = *(lname++);
+
+  return ptr;
+}
+
+
+
+static void
+check_status (Context * c, Clients * cs)
+{
+  static Status old_status = { 0 };
+  Status status;
+  char buf[1024];
+  char *ptr = buf;
+  char *t;
+
+  status = get_status (c->t, cs);
+  if (!memcmp (&status, &old_status, sizeof (status)))
+    return;
+
+  log_line_changes (c, old_status.lines, status.lines);
+
+  old_status = status;
+
+  ptr += sprintf (ptr, "CTRL-B ");
+
+  t = c->t->name;
+  if (!strncmp (t, "/dev/", 5))
+    t += 5;
+  while (*t)
+    *(ptr++) = *(t++);
+
+  ptr += sprintf (ptr, " %db", status.baud);
+
+  ptr = do_line (ptr, status.lines, TIOCM_RTS);
+  ptr = do_line (ptr, status.lines, TIOCM_CTS);
+  ptr = do_line (ptr, status.lines, TIOCM_DTR);
+  ptr = do_line (ptr, status.lines, TIOCM_DSR);
+  ptr = do_line (ptr, status.lines, TIOCM_RI);
+  ptr = do_line (ptr, status.lines, TIOCM_CD);
+
+  if (status.blocked)
+    {
+      t = ", Locked";
+      while (*t)
+        *(ptr++) = *(t++);
+    }
+
+  if (status.crtscts)
+    {
+      t = ", Flow";
+      while (*t)
+        *(ptr++) = *(t++);
+    }
+
+#if 0
+  if (status.lines & TIOCM_CD)
+    {
+      ptr +=
+        sprintf (ptr, ", On %d.%d", status.cd_edge_sec / 60,
+                 status.cd_edge_sec % 60);
+    }
+  else
+    {
+      ptr +=
+        sprintf (ptr, ", Off %d.%d", status.cd_edge_sec / 60,
+                 status.cd_edge_sec % 60);
+    }
+#endif
+
+  ptr +=
+    sprintf (ptr, ", %d client%s", status.nclients,
+             (status.nclients == 1) ? "" : "s");
+
+  if (c->tp->biterrs)
+    {
+
+      ptr +=
+        sprintf (ptr, ", %d err%s", c->tp->biterrs,
+                 (c->tp->biterrs == 1) ? "" : "s");
+
+      if (c->tp->guessed_baud == -1)
+        {
+          ptr += sprintf (ptr, " try higher");
+        }
+      else if (c->tp->guessed_baud > 0)
+        {
+          ptr += sprintf (ptr, " try %db", c->tp->guessed_baud);
+        }
+    }
+
+  *ptr = 0;
+
+#if 0
+  log_f (c->l, "%s:%d %s", __FILE__, __LINE__, buf);
+#endif
+
+  if (cs)
+    send_status (cs, buf);
+  else
+    cmd_new_status (c->d, c, buf);
+
+}
+
+
+static int
+msg_from_server (ANSI * a, IPC_Msg * m, Context * c)
+{
+  switch (m->hdr.type)
+    {
+
+    case IPC_MSG_TYPE_NOOP:
+      break;
+    case IPC_MSG_TYPE_DEBUG:
+//          fprintf (stderr,"%p [%d] %s\n", m, m->hdr.size , m->debug.msg );
+      break;
+    case IPC_MSG_TYPE_HISTORY:
+      history_add (c->h, m->history.history.line);
+      break;
+    case IPC_MSG_TYPE_VT102:
+      if (sizeof (VT102) != m->vt102.len)
+        abort ();
+
+      *(c->v) = m->vt102.vt102;
+
+      if (a->one_shot)
+        {
+          a->one_shot (a, &c->v->crt);
+          return 1;
+        }
+      //FIXME HTML hook
+      break;
+    case IPC_MSG_TYPE_TERM:
+      tty_parse (c, m->term.term, m->term.len);
+      break;
+    case IPC_MSG_TYPE_STATUS:
+      cmd_new_status (c->d, c, m->status.status);
+      break;
+    default:
+      fprintf (stderr, "Unhandeled message type %d\n", m->hdr.type);
+    }
+  return 0;
+}
+
+
+void
+mainloop (TTY * tty, Socket * server_socket, Socket * client_socket,
+          ANSI * ansi, Log * log, int nhistory, CRT_Pos *size )
+{
+  fd_set rfds, wfds;
+  Context c = { 0 };
+  Clients *clients;
+
+  context = &c;
+
+  c.tp = tty_parser_new ();
+  c.u = utf8_new ();
+
+  c.v = vt102_new (size);
+  c.h = history_new (nhistory);
+  c.l = log;
+  /* are we being fed by a tty or a socket */
+  if (client_socket)
+    {
+      if (server_socket)
+        abort ();
+      c.k = keydis_ipc_new (client_socket);
+    }
+  else
+    {
+      if (!tty)
+        abort ();
+      c.t = tty;
+      c.k = keydis_vt102_new ();
+    }
+
+  /* do we have an upstream terminal to talk to */
+  /* if so start a command parser */
+  if (ansi)
+    {
+      c.d = cmd_new ();
+    }
+  else
+    {
+      c.d = NULL;
+    }
+
+
+  if (server_socket)
+    {
+      if (client_socket)
+        abort ();
+      clients = clients_new ();
+    }
+  else
+    {
+      clients = NULL;
+    }
+
+  for (;;)
+    {
+      struct timeval tv = { 0, 250000 };
+
+      if ((c.d) && (c.d->disconnect))
+        break;
+
+      /*update the status lines, locally or remotely */
+      if (c.t)
+        check_status (&c, clients);
+
+      FD_ZERO (&rfds);
+      FD_ZERO (&wfds);
+
+      if (c.t)
+        tty_pre_select (c.t, &rfds, &wfds);
+
+      if (server_socket)
+        {
+          FD_SET (server_socket->fd, &rfds);
+          clients_pre_select (clients, &rfds, &wfds);
+        }
+
+      if (client_socket)
+        socket_pre_select (client_socket, &rfds, &wfds);
+
+      if (ansi && ansi->terminal)
+        tty_pre_select (ansi->terminal, &rfds, &wfds);
+
+      select (FD_SETSIZE, &rfds, &wfds, NULL, &tv);
+
+      /*any message from clients, or new connexions */
+      if (server_socket)
+        {
+          Socket *new_client_socket;
+          if (FD_ISSET (server_socket->fd, &rfds)
+              && ((new_client_socket = socket_accept (server_socket))))
+            {
+              {
+                Client *new_client;
+                /*New client connexion */
+                new_client =
+                  clients_new_client (clients, new_client_socket, &c);
+
+                send_history (c.h, new_client);
+                send_vt102 (c.v, new_client);
+
+              }
+            }
+
+          clients_post_select (clients, &c, &rfds, &wfds);
+        }
+
+      /*any data from the port */
+      if (c.t && FD_ISSET (c.t->rfd, &rfds))
+        {
+          char buf[IPC_MAX_BUF];
+          int red;
+
+          red = c.t->recv (c.t, buf, sizeof (buf));
+
+          if (red < 0)
+            break;
+
+          if (red)
+            {
+              if (clients)
+                send_output (clients, buf, red);
+              tty_parse (&c, buf, red);
+            }
+        }
+
+
+
+      /*any data from the server */
+      if (client_socket)
+        {
+          int one_shot;
+          if (socket_post_select (client_socket, &rfds, &wfds))
+            break;
+
+          while (client_socket->msg)
+            {
+              if (msg_from_server (ansi, client_socket->msg, &c))
+                one_shot++;
+
+              socket_consume_msg (client_socket);
+            }
+          if (one_shot)
+            break;
+        }
+
+
+      /*update our local screen */
+      if (ansi)
+        {
+          if (ansi->dispatch)
+            ansi->dispatch (ansi, &c);
+
+          if (ansi->update)
+            ansi->update (ansi, &c);
+        }
+    }
+
+  if (clients)
+    clients_shutdown (clients);
+}