source: select.c @ 54b4a87

release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since 54b4a87 was b752f1e, checked in by Nelson Elhage <nelhage@mit.edu>, 12 years ago
Allow ^Z to be rebound. Rather than catching SIGTSTP and doing things with it, disable VSUSP, which causes ^Z to get passed through to us as a normal keystroke. In addition, change the binding so that it prints the warning SIGTSTP used to.
  • Property mode set to 100644
File size: 11.7 KB
Line 
1#include "owl.h"
2
3static int dispatch_active = 0;
4static int psa_active = 0;
5
6int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
7  return t1->time - t2->time;
8}
9
10int _owl_select_timer_eq(const owl_timer *t1, const owl_timer *t2) {
11  return t1 == t2;
12}
13
14owl_timer *owl_select_add_timer(int after, int interval, void (*cb)(owl_timer *, void *), void (*destroy)(owl_timer*), void *data)
15{
16  owl_timer *t = owl_malloc(sizeof(owl_timer));
17  GList **timers = owl_global_get_timerlist(&g);
18
19  t->time = time(NULL) + after;
20  t->interval = interval;
21  t->callback = cb;
22  t->destroy = destroy;
23  t->data = data;
24
25  *timers = g_list_insert_sorted(*timers, t,
26                                 (GCompareFunc)_owl_select_timer_cmp);
27  return t;
28}
29
30void owl_select_remove_timer(owl_timer *t)
31{
32  GList **timers = owl_global_get_timerlist(&g);
33  if (t && g_list_find(*timers, t)) {
34    *timers = g_list_remove(*timers, t);
35    if(t->destroy) {
36      t->destroy(t);
37    }
38    owl_free(t);
39  }
40}
41
42void owl_select_process_timers(struct timespec *timeout)
43{
44  time_t now = time(NULL);
45  GList **timers = owl_global_get_timerlist(&g);
46
47  while(*timers) {
48    owl_timer *t = (*timers)->data;
49    int remove = 0;
50
51    if(t->time > now)
52      break;
53
54    /* Reschedule if appropriate */
55    if(t->interval > 0) {
56      t->time = now + t->interval;
57      *timers = g_list_remove(*timers, t);
58      *timers = g_list_insert_sorted(*timers, t,
59                                     (GCompareFunc)_owl_select_timer_cmp);
60    } else {
61      remove = 1;
62    }
63
64    /* Do the callback */
65    t->callback(t, t->data);
66    if(remove) {
67      owl_select_remove_timer(t);
68    }
69  }
70
71  if(*timers) {
72    owl_timer *t = (*timers)->data;
73    timeout->tv_sec = t->time - now;
74    if (timeout->tv_sec > 60)
75      timeout->tv_sec = 60;
76  } else {
77    timeout->tv_sec = 60;
78  }
79
80  timeout->tv_nsec = 0;
81}
82
83static const owl_io_dispatch *owl_select_find_io_dispatch_by_fd(const int fd)
84{
85  int i, len;
86  const owl_list *dl;
87  owl_io_dispatch *d;
88  dl = owl_global_get_io_dispatch_list(&g);
89  len = owl_list_get_size(dl);
90  for(i = 0; i < len; i++) {
91    d = owl_list_get_element(dl, i);
92    if (d->fd == fd) return d;
93  }
94  return NULL;
95}
96
97static int owl_select_find_io_dispatch(const owl_io_dispatch *in)
98{
99  int i, len;
100  const owl_list *dl;
101
102  if (in != NULL) {
103    dl = owl_global_get_io_dispatch_list(&g);
104    len = owl_list_get_size(dl);
105    for(i = 0; i < len; i++) {
106      const owl_io_dispatch *d = owl_list_get_element(dl, i);
107      if (d == in) return i;
108    }
109  }
110  return -1;
111}
112
113void owl_select_remove_io_dispatch(const owl_io_dispatch *in)
114{
115  int elt;
116  if (in != NULL) {
117    elt = owl_select_find_io_dispatch(in);
118    if (elt != -1) {
119      owl_list *dl = owl_global_get_io_dispatch_list(&g);
120      owl_io_dispatch *d = owl_list_get_element(dl, elt);
121      if (dispatch_active)
122        d->needs_gc = 1;
123      else {
124        owl_list_remove_element(dl, elt);
125        if (d->destroy)
126          d->destroy(d);
127        owl_free(d);
128      }
129    }
130  }
131}
132
133void owl_select_io_dispatch_gc(void)
134{
135  int i;
136  owl_list *dl;
137
138  dl = owl_global_get_io_dispatch_list(&g);
139  /*
140   * Count down so we aren't set off by removing items from the list
141   * during the iteration.
142   */
143  for(i = owl_list_get_size(dl) - 1; i >= 0; i--) {
144    owl_io_dispatch *d = owl_list_get_element(dl, i);
145    if(d->needs_gc) {
146      owl_select_remove_io_dispatch(d);
147    }
148  }
149}
150
151/* Each FD may have at most one dispatcher.
152 * If a new dispatch is added for an FD, the old one is removed.
153 * mode determines what types of events are watched for, and may be any combination of:
154 * OWL_IO_READ, OWL_IO_WRITE, OWL_IO_EXCEPT
155 */
156const owl_io_dispatch *owl_select_add_io_dispatch(int fd, int mode, void (*cb)(const owl_io_dispatch *, void *), void (*destroy)(const owl_io_dispatch *), void *data)
157{
158  owl_io_dispatch *d = owl_malloc(sizeof(owl_io_dispatch));
159  owl_list *dl = owl_global_get_io_dispatch_list(&g);
160
161  d->fd = fd;
162  d->needs_gc = 0;
163  d->mode = mode;
164  d->callback = cb;
165  d->destroy = destroy;
166  d->data = data;
167
168  owl_select_remove_io_dispatch(owl_select_find_io_dispatch_by_fd(fd));
169  owl_list_append_element(dl, d);
170
171  return d;
172}
173
174int owl_select_prepare_io_dispatch_fd_sets(fd_set *rfds, fd_set *wfds, fd_set *efds) {
175  int i, len, max_fd;
176  owl_io_dispatch *d;
177  owl_list *dl = owl_global_get_io_dispatch_list(&g);
178
179  max_fd = 0;
180  len = owl_list_get_size(dl);
181  for (i = 0; i < len; i++) {
182    d = owl_list_get_element(dl, i);
183    if (d->mode & (OWL_IO_READ | OWL_IO_WRITE | OWL_IO_EXCEPT)) {
184      if (max_fd < d->fd) max_fd = d->fd;
185      if (d->mode & OWL_IO_READ) FD_SET(d->fd, rfds);
186      if (d->mode & OWL_IO_WRITE) FD_SET(d->fd, wfds);
187      if (d->mode & OWL_IO_EXCEPT) FD_SET(d->fd, efds);
188    }
189  }
190  return max_fd + 1;
191}
192
193void owl_select_io_dispatch(const fd_set *rfds, const fd_set *wfds, const fd_set *efds, const int max_fd)
194{
195  int i, len;
196  owl_io_dispatch *d;
197  owl_list *dl = owl_global_get_io_dispatch_list(&g);
198
199  dispatch_active = 1;
200  len = owl_list_get_size(dl);
201  for (i = 0; i < len; i++) {
202    d = owl_list_get_element(dl, i);
203    if (d->fd < max_fd && d->callback != NULL &&
204        ((d->mode & OWL_IO_READ && FD_ISSET(d->fd, rfds)) ||
205         (d->mode & OWL_IO_WRITE && FD_ISSET(d->fd, wfds)) ||
206         (d->mode & OWL_IO_EXCEPT && FD_ISSET(d->fd, efds)))) {
207      d->callback(d, d->data);
208    }
209  }
210  dispatch_active = 0;
211  owl_select_io_dispatch_gc();
212}
213
214int owl_select_add_perl_io_dispatch(int fd, int mode, SV *cb)
215{
216  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
217  if (d != NULL && d->callback != owl_perlconfig_io_dispatch) {
218    /* Don't mess with non-perl dispatch functions from here. */
219    return 1;
220  }
221  owl_select_add_io_dispatch(fd, mode, owl_perlconfig_io_dispatch, owl_perlconfig_io_dispatch_destroy, cb);
222  return 0;
223}
224
225int owl_select_remove_perl_io_dispatch(int fd)
226{
227  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
228  if (d != NULL && d->callback == owl_perlconfig_io_dispatch) {
229    /* Only remove perl io dispatchers from here. */
230    owl_select_remove_io_dispatch(d);
231    return 0;
232  }
233  return 1;
234}
235
236int owl_select_aim_hack(fd_set *rfds, fd_set *wfds)
237{
238  aim_conn_t *cur;
239  aim_session_t *sess;
240  int max_fd;
241
242  max_fd = 0;
243  sess = owl_global_get_aimsess(&g);
244  for (cur = sess->connlist, max_fd = 0; cur; cur = cur->next) {
245    if (cur->fd != -1) {
246      FD_SET(cur->fd, rfds);
247      if (cur->status & AIM_CONN_STATUS_INPROGRESS) {
248        /* Yes, we're checking writable sockets here. Without it, AIM
249           login is really slow. */
250        FD_SET(cur->fd, wfds);
251      }
252     
253      if (cur->fd > max_fd)
254        max_fd = cur->fd;
255    }
256  }
257  return max_fd;
258}
259
260void owl_process_input_char(owl_input j)
261{
262  int ret;
263  owl_popwin *pw;
264  owl_editwin *tw;
265
266  owl_global_set_lastinputtime(&g, time(NULL));
267  pw=owl_global_get_popwin(&g);
268  tw=owl_global_get_typwin(&g);
269
270  owl_global_set_lastinputtime(&g, time(NULL));
271  /* find and activate the current keymap.
272   * TODO: this should really get fixed by activating
273   * keymaps as we switch between windows...
274   */
275  if (pw && owl_popwin_is_active(pw) && owl_global_get_viewwin(&g)) {
276    owl_context_set_popless(owl_global_get_context(&g), 
277                            owl_global_get_viewwin(&g));
278    owl_function_activate_keymap("popless");
279  } else if (owl_global_is_typwin_active(&g) 
280             && owl_editwin_get_style(tw)==OWL_EDITWIN_STYLE_ONELINE) {
281    /*
282      owl_context_set_editline(owl_global_get_context(&g), tw);
283      owl_function_activate_keymap("editline");
284    */
285  } else if (owl_global_is_typwin_active(&g) 
286             && owl_editwin_get_style(tw)==OWL_EDITWIN_STYLE_MULTILINE) {
287    owl_context_set_editmulti(owl_global_get_context(&g), tw);
288    owl_function_activate_keymap("editmulti");
289  } else {
290    owl_context_set_recv(owl_global_get_context(&g));
291    owl_function_activate_keymap("recv");
292  }
293  /* now actually handle the keypress */
294  ret = owl_keyhandler_process(owl_global_get_keyhandler(&g), j);
295  if (ret!=0 && ret!=1) {
296    owl_function_makemsg("Unable to handle keypress");
297  }
298}
299
300void owl_select_mask_signals(sigset_t *oldmask) {
301  sigset_t set;
302
303  sigemptyset(&set);
304  sigaddset(&set, SIGINT);
305  sigprocmask(SIG_BLOCK, &set, oldmask);
306}
307
308void owl_select_handle_intr(sigset_t *restore)
309{
310  owl_input in;
311
312  owl_global_unset_interrupted(&g);
313
314  sigprocmask(SIG_SETMASK, restore, NULL);
315
316  in.ch = in.uch = owl_global_get_startup_tio(&g)->c_cc[VINTR];
317  owl_process_input_char(in);
318}
319
320owl_ps_action *owl_select_add_pre_select_action(int (*cb)(owl_ps_action *, void *), void (*destroy)(owl_ps_action *), void *data)
321{
322  owl_ps_action *a = owl_malloc(sizeof(owl_ps_action));
323  owl_list *psa_list = owl_global_get_psa_list(&g);
324  a->needs_gc = 0;
325  a->callback = cb;
326  a->destroy = destroy;
327  a->data = data;
328  owl_list_append_element(psa_list, a);
329  return a;
330}
331
332void owl_select_psa_gc(void)
333{
334  int i;
335  owl_list *psa_list;
336  owl_ps_action *a;
337
338  psa_list = owl_global_get_psa_list(&g);
339  for (i = owl_list_get_size(psa_list) - 1; i >= 0; i--) {
340    a = owl_list_get_element(psa_list, i);
341    if (a->needs_gc) {
342      owl_list_remove_element(psa_list, i);
343      if (a->destroy) {
344        a->destroy(a);
345      }
346      owl_free(a);
347    }
348  }
349}
350
351void owl_select_remove_pre_select_action(owl_ps_action *a)
352{
353  a->needs_gc = 1;
354  if (!psa_active)
355    owl_select_psa_gc();
356}
357
358int owl_select_do_pre_select_actions(void)
359{
360  int i, len, ret;
361  owl_list *psa_list;
362
363  psa_active = 1;
364  ret = 0;
365  psa_list = owl_global_get_psa_list(&g);
366  len = owl_list_get_size(psa_list);
367  for (i = 0; i < len; i++) {
368    owl_ps_action *a = owl_list_get_element(psa_list, i);
369    if (a->callback != NULL && a->callback(a, a->data)) {
370      ret = 1;
371    }
372  }
373  psa_active = 0;
374  owl_select_psa_gc();
375  return ret;
376}
377
378void owl_select(void)
379{
380  int i, max_fd, max_fd2, aim_done, ret;
381  fd_set r;
382  fd_set w;
383  fd_set e;
384  fd_set aim_rfds, aim_wfds;
385  struct timespec timeout;
386  sigset_t mask;
387
388  owl_select_process_timers(&timeout);
389
390  owl_select_mask_signals(&mask);
391
392  if(owl_global_is_interrupted(&g)) {
393    owl_select_handle_intr(&mask);
394    return;
395  }
396  FD_ZERO(&r);
397  FD_ZERO(&w);
398  FD_ZERO(&e);
399
400  max_fd = owl_select_prepare_io_dispatch_fd_sets(&r, &w, &e);
401
402  /* AIM HACK:
403   *
404   *  The problem - I'm not sure where to hook into the owl/faim
405   *  interface to keep track of when the AIM socket(s) open and
406   *  close. In particular, the bosconn thing throws me off. So,
407   *  rather than register particular dispatchers for AIM, I look up
408   *  the relevant FDs and add them to select's watch lists, then
409   *  check for them individually before moving on to the other
410   *  dispatchers. --asedeno
411   */
412  aim_done = 1;
413  FD_ZERO(&aim_rfds);
414  FD_ZERO(&aim_wfds);
415  if (owl_global_is_doaimevents(&g)) {
416    aim_done = 0;
417    max_fd2 = owl_select_aim_hack(&aim_rfds, &aim_wfds);
418    if (max_fd < max_fd2) max_fd = max_fd2;
419    for(i = 0; i <= max_fd2; i++) {
420      if (FD_ISSET(i, &aim_rfds)) {
421        FD_SET(i, &r);
422        FD_SET(i, &e);
423      }
424      if (FD_ISSET(i, &aim_wfds)) {
425        FD_SET(i, &w);
426        FD_SET(i, &e);
427      }
428    }
429  }
430  /* END AIM HACK */
431
432  if (owl_select_do_pre_select_actions()) {
433    timeout.tv_sec = 0;
434    timeout.tv_nsec = 0;
435  }
436
437  ret = pselect(max_fd+1, &r, &w, &e, &timeout, &mask);
438
439  if(ret < 0 && errno == EINTR) {
440    if(owl_global_is_interrupted(&g)) {
441      owl_select_handle_intr(NULL);
442    }
443    sigprocmask(SIG_SETMASK, &mask, NULL);
444    return;
445  }
446
447  sigprocmask(SIG_SETMASK, &mask, NULL);
448
449  if(ret > 0) {
450    /* AIM HACK: process all AIM events at once. */
451    for(i = 0; !aim_done && i <= max_fd; i++) {
452      if (FD_ISSET(i, &r) || FD_ISSET(i, &w) || FD_ISSET(i, &e)) {
453        if (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds)) {
454          owl_process_aim();
455          aim_done = 1;
456        }
457      }
458    }
459    owl_select_io_dispatch(&r, &w, &e, max_fd);
460  }
461}
Note: See TracBrowser for help on using the repository browser.