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