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