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