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