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