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