chiark / gitweb /
*** empty log message ***
[sympathy.git] / apps / mainloop.c
1 /*
2  * mainloop.c:
3  *
4  * Copyright (c) 2008 James McKenzie <james@fishsoup.dhs.org>,
5  * All rights reserved.
6  *
7  */
8
9 static char rcsid[] = "$Id$";
10
11 /*
12  * $Log$
13  * Revision 1.7  2008/02/20 20:16:07  james
14  * *** empty log message ***
15  *
16  * Revision 1.6  2008/02/20 19:44:37  james
17  * @@
18  *
19  * Revision 1.5  2008/02/20 18:31:44  james
20  * *** empty log message ***
21  *
22  * Revision 1.4  2008/02/20 17:18:33  james
23  * *** empty log message ***
24  *
25  * Revision 1.3  2008/02/20 02:11:35  james
26  * *** empty log message ***
27  *
28  * Revision 1.2  2008/02/16 10:58:52  james
29  * *** empty log message ***
30  *
31  * Revision 1.13  2008/02/15 23:52:12  james
32  * *** empty log message ***
33  *
34  * Revision 1.12  2008/02/15 03:32:07  james
35  * *** empty log message ***
36  *
37  * Revision 1.11  2008/02/14 16:21:17  james
38  * *** empty log message ***
39  *
40  * Revision 1.10  2008/02/14 10:39:14  james
41  * *** empty log message ***
42  *
43  * Revision 1.9  2008/02/14 10:34:47  james
44  * *** empty log message ***
45  *
46  * Revision 1.8  2008/02/14 10:34:30  james
47  * *** empty log message ***
48  *
49  * Revision 1.7  2008/02/14 02:46:44  james
50  * *** empty log message ***
51  *
52  * Revision 1.6  2008/02/14 00:57:58  james
53  * *** empty log message ***
54  *
55  * Revision 1.5  2008/02/13 18:05:06  james
56  * *** empty log message ***
57  *
58  * Revision 1.4  2008/02/13 17:21:55  james
59  * *** empty log message ***
60  *
61  * Revision 1.3  2008/02/08 15:06:52  james
62  * *** empty log message ***
63  *
64  * Revision 1.2  2008/02/07 15:42:49  james
65  * *** empty log message ***
66  *
67  * Revision 1.1  2008/02/05 14:25:49  james
68  * *** empty log message ***
69  *
70  */
71
72 #include <sys/time.h>
73 #include <sympathy.h>
74 #include "mainloop.h"
75
76 #include "clients.h"
77
78 typedef struct
79 {
80   int nclients;
81   int lines;
82   int baud;
83   int crtscts;
84   int cd_edge_sec;
85   int blocked;
86   int bootstrap;
87 } Status;
88
89 static Status
90 get_status (TTY * t, Clients * cs)
91 {
92   static struct timeval last_cd_edge = { 0 };
93   static int last_cd_state = -1;
94   int cd;
95   struct timeval now, dif;
96
97   TTY_Status tty_status = { 0 };
98   Status status;
99
100   tty_get_status (t, &tty_status);
101
102   status.bootstrap = 1;
103
104   if (cs)
105     status.nclients = cs->n;
106   else
107     status.nclients = 0;
108
109   status.lines = tty_status.lines;
110   status.baud = tty_status.baud;
111   status.crtscts = (tty_status.termios.c_cflag & CRTSCTS) ? 1 : 0;
112   status.blocked = tty_status.blocked;
113
114   cd = (tty_status.lines & TIOCM_CD) ? 1 : 0;
115
116   if (cd != last_cd_state)
117     {
118       gettimeofday (&last_cd_edge, NULL);
119       last_cd_state = cd;
120     }
121
122   gettimeofday (&now, NULL);
123   timersub (&now, &last_cd_edge, &dif);
124   status.cd_edge_sec = dif.tv_sec;
125
126   return status;
127 }
128
129 static char *
130 line_to_name (int l)
131 {
132
133   switch (l)
134     {
135 #ifdef TIOCM_LE
136     case TIOCM_LE:
137       return "LE";
138 #endif
139 #ifdef TIOCM_DTR
140     case TIOCM_DTR:
141       return "DTR";
142 #endif
143 #ifdef TIOCM_RTS
144     case TIOCM_RTS:
145       return "RTS";
146 #endif
147 #ifdef TIOCM_ST
148     case TIOCM_ST:
149       return "ST";
150 #endif
151 #ifdef TIOCM_SR
152     case TIOCM_SR:
153       return "SR";
154 #endif
155 #ifdef TIOCM_CTS
156     case TIOCM_CTS:
157       return "CTS";
158 #endif
159 #ifdef TIOCM_CD
160     case TIOCM_CD:
161       return "CD";
162 #endif
163 #ifdef TIOCM_RI
164     case TIOCM_RI:
165       return "RI";
166 #endif
167 #ifdef TIOCM_DSR
168     case TIOCM_DSR:
169       return "DSR";
170 #endif
171     }
172   return "??";
173 }
174
175 static void
176 log_line_changes (Context * ctx, int old, int new)
177 {
178   int dif = old ^ new;
179   int c = 1;
180   char buf[1024], *ptr = buf;
181   char *n;
182
183   if (!dif)
184     return;
185   if (!ctx->l)
186     return;
187
188   n = "<Modem lines changed:";
189
190   while (*n)
191     *(ptr++) = *(n++);
192
193   while (dif >= c)
194     {
195
196       if (dif & c)
197         {
198           *(ptr++) = ' ';
199           *(ptr++) = (new & c) ? '+' : '-';
200           n = line_to_name (c);
201           while (*n)
202             *(ptr++) = *(n++);
203         }
204
205       c <<= 1;
206     }
207   *(ptr++) = '>';
208   *ptr = 0;
209
210
211   ctx->l->log (ctx->l, buf);
212
213 }
214
215 static char *
216 do_line (char *ptr, int lines, int line)
217 {
218   char *lname;
219
220   if (!(lines & line))
221     return ptr;
222   lname = line_to_name (line);
223
224   *(ptr++) = ' ';
225   while (*lname)
226     *(ptr++) = *(lname++);
227
228   return ptr;
229 }
230
231
232
233 static void
234 check_status (Context * c, Clients * cs)
235 {
236   static Status old_status = { 0 };
237   Status status;
238   char buf[1024];
239   char *ptr = buf;
240   char *t;
241
242   status = get_status (c->t, cs);
243   if (!memcmp (&status, &old_status, sizeof (status)))
244     return;
245
246   log_line_changes (c, old_status.lines, status.lines);
247
248   old_status = status;
249
250   ptr += sprintf (ptr, "CTRL-B ");
251
252   t = c->t->name;
253   if (!strncmp (t, "/dev/", 5))
254     t += 5;
255   while (*t)
256     *(ptr++) = *(t++);
257
258   ptr += sprintf (ptr, " %db", status.baud);
259
260   ptr = do_line (ptr, status.lines, TIOCM_RTS);
261   ptr = do_line (ptr, status.lines, TIOCM_CTS);
262   ptr = do_line (ptr, status.lines, TIOCM_DTR);
263   ptr = do_line (ptr, status.lines, TIOCM_DSR);
264   ptr = do_line (ptr, status.lines, TIOCM_RI);
265   ptr = do_line (ptr, status.lines, TIOCM_CD);
266
267   if (status.blocked)
268     {
269       t = ", Locked";
270       while (*t)
271         *(ptr++) = *(t++);
272     }
273
274   if (status.crtscts)
275     {
276       t = ", Flow";
277       while (*t)
278         *(ptr++) = *(t++);
279     }
280
281 #if 0
282   if (status.lines & TIOCM_CD)
283     {
284       ptr +=
285         sprintf (ptr, ", On %d.%d", status.cd_edge_sec / 60,
286                  status.cd_edge_sec % 60);
287     }
288   else
289     {
290       ptr +=
291         sprintf (ptr, ", Off %d.%d", status.cd_edge_sec / 60,
292                  status.cd_edge_sec % 60);
293     }
294 #endif
295
296   ptr +=
297     sprintf (ptr, ", %d client%s", status.nclients,
298              (status.nclients == 1) ? "" : "s");
299
300   *ptr = 0;
301
302   if (cs)
303     send_status (cs, buf);
304   else
305     cmd_new_status (c->d, c, buf);
306
307 }
308
309
310 static int
311 msg_from_server (ANSI *a,IPC_Msg * m, Context * c)
312 {
313   switch (m->hdr.type)
314     {
315
316     case IPC_MSG_TYPE_NOOP:
317       break;
318     case IPC_MSG_TYPE_DEBUG:
319 //          fprintf (stderr,"%p [%d] %s\n", m, m->hdr.size , m->debug.msg );
320       break;
321     case IPC_MSG_TYPE_HISTORY:
322       history_add (c->h, m->history.history.line);
323       break;
324     case IPC_MSG_TYPE_VT102:
325       if (sizeof (VT102) != m->vt102.len)
326         abort ();
327
328       *(c->v) = m->vt102.vt102;
329
330       if (a->one_shot) {
331                 a->one_shot(a,&c->v->crt);
332                 return 1;
333         }
334       //FIXME HTML hook
335       break;
336     case IPC_MSG_TYPE_TERM:
337       vt102_parse (c, m->term.term, m->term.len);
338       break;
339     case IPC_MSG_TYPE_STATUS:
340       cmd_new_status (c->d, c, m->status.status);
341       break;
342     default:
343       fprintf (stderr, "Unhandeled message type %d\n", m->hdr.type);
344     }
345 return 0;
346 }
347
348
349 void
350 mainloop (TTY * tty, Socket * server_socket, Socket * client_socket,
351           ANSI * ansi, Log * log, int nhistory)
352 {
353   fd_set rfds, wfds;
354   Context c = { 0 };
355   Clients *clients;
356
357
358   c.v = vt102_new ();
359   c.h = history_new (nhistory);
360   c.l = log;
361   /* are we being fed by a tty or a socket */
362   if (client_socket)
363     {
364       if (server_socket)
365         abort ();
366       c.k = keydis_ipc_new (client_socket);
367     }
368   else
369     {
370       if (!tty)
371         abort ();
372       c.t = tty;
373       c.k = keydis_vt102_new ();
374     }
375
376   /* do we have an upstream terminal to talk to */
377   /* if so start a command parser */
378   if (ansi)
379     {
380       c.d = cmd_new ();
381     }
382   else
383     {
384       c.d = NULL;
385     }
386
387
388   if (server_socket)
389     {
390       if (client_socket)
391         abort ();
392       clients = clients_new ();
393     }
394   else
395     {
396       clients = NULL;
397     }
398
399   for (;;)
400     {
401       struct timeval tv = { 1, 0 };
402
403       if ((c.d) && (c.d->disconnect))
404         break;
405
406       /*update the status lines, locally or remotely */
407       if (c.t)
408         check_status (&c, clients);
409
410       FD_ZERO (&rfds);
411       FD_ZERO (&wfds);
412
413       if (c.t)
414         tty_pre_select (c.t, &rfds, &wfds);
415
416       if (server_socket)
417         {
418           FD_SET (server_socket->fd, &rfds);
419           clients_pre_select (clients, &rfds, &wfds);
420         }
421
422       if (client_socket)
423         socket_pre_select (client_socket, &rfds, &wfds);
424
425       if (ansi && ansi->terminal)
426         tty_pre_select (ansi->terminal, &rfds, &wfds);
427
428       select (FD_SETSIZE, &rfds, &wfds, NULL, &tv);
429
430       /*any message from clients, or new connexions */
431       if (server_socket)
432         {
433           Socket *new_client_socket;
434           if (FD_ISSET (server_socket->fd, &rfds)
435               && ((new_client_socket = socket_accept (server_socket))))
436             {
437               {
438                 Client *new_client;
439                 /*New client connexion */
440                 new_client =
441                   clients_new_client (clients, new_client_socket, &c);
442
443                 send_history (c.h, new_client);
444                 send_vt102 (c.v, new_client);
445
446               }
447             }
448
449           clients_post_select (clients, &c, &rfds, &wfds);
450         }
451
452       /*any data from the port */
453       if (c.t && FD_ISSET (c.t->rfd, &rfds))
454         {
455           char buf[IPC_MAX_BUF];
456           int red;
457
458           red = c.t->recv (c.t, buf, sizeof (buf));
459
460           if (red < 0)
461             break;
462
463           if (red)
464             {
465               if (clients)
466                 send_output (clients, buf, red);
467               vt102_parse (&c, buf, red);
468             }
469         }
470
471
472
473       /*any data from the server */
474       if (client_socket)
475         {
476           int one_shot;
477           if (socket_post_select (client_socket, &rfds, &wfds))
478             break;
479
480           while (client_socket->msg)
481             {
482               if (msg_from_server (ansi,client_socket->msg, &c))
483                 one_shot++;
484                 
485               socket_consume_msg (client_socket);
486             }
487                 if (one_shot) break;
488         }
489
490
491       /*update our local screen */
492       if (ansi)
493         {
494           if (ansi->dispatch)
495                 ansi->dispatch (ansi, &c);
496
497           if (ansi->update)
498           ansi->update (ansi, &c);
499         }
500     }
501
502   if (clients)
503     clients_shutdown (clients);
504 }