1 #include "tickit.h"
2 #include "tickit-evloop.h"
3
4 #include <errno.h>
5 #include <signal.h>
6 #include <string.h>
7 #include <sys/time.h>
8 #include <sys/wait.h>
9 #include <unistd.h>
10
11 /* POSIX doesn't actually define an NSIG macro, but if it did, almost every
12 * known platform would set it to 32
13 */
14 #ifndef NSIG
15 # define NSIG 32
16 #endif
17
18 #define streq(a,b) (!strcmp(a,b))
19
20 /* INTERNAL */
21 TickitWindow* tickit_window_new_root2(Tickit *t, TickitTerm *term);
22
23 struct TickitWatch {
24 TickitWatch *next;
25
26 Tickit *t; // uncounted
27
28 enum {
29 WATCH_NONE,
30 WATCH_IO,
31 WATCH_TIMER,
32 WATCH_LATER,
33 WATCH_SIGNAL,
34 WATCH_PROCESS,
35 } type;
36
37 TickitBindFlags flags;
38 TickitCallbackFn *fn;
39 void *user;
40
41 union {
42 void *ptr;
43 int i;
44 } evdata;
45
46 union {
47 struct {
48 int fd;
49 TickitIOCondition cond;
50 } io;
51
52 struct {
53 struct timeval at;
54 } timer;
55
56 struct {
57 int signum;
58 } signal;
59
60 struct {
61 pid_t pid;
62 int wstatus; /* in case of pre-exited process */
63 } process;
64 };
65 };
66
67 extern TickitEventHooks tickit_evloop_default;
68
69 struct Tickit {
70 int refcount;
71
72 TickitTerm *term;
73 TickitWindow *rootwin;
74
75 TickitWatch *iowatches, *timers, *laters, *signals, *processes;
76
77 const TickitEventHooks *evhooks;
78 void *evdata;
79
80 struct {
81 int pipefds[2];
82 void *pipewatch;
83 sigset_t watched;
84 sigset_t pending;
85 } signal;
86
87 void *sigchldwatch;
88
89 unsigned int done_setup : 1,
90 use_altscreen : 1;
91 };
92
93 static int on_term_timeout(Tickit *t, TickitEventFlags flags, void *info, void *user);
on_term_timeout(Tickit * t,TickitEventFlags flags,void * info,void * user)94 static int on_term_timeout(Tickit *t, TickitEventFlags flags, void *info, void *user)
95 {
96 int timeout = tickit_term_input_check_timeout_msec(t->term);
97 if(timeout > -1)
98 tickit_watch_timer_after_msec(t, timeout, 0, on_term_timeout, NULL);
99
100 return 0;
101 }
102
on_term_readable(Tickit * t,TickitEventFlags flags,void * info,void * user)103 static int on_term_readable(Tickit *t, TickitEventFlags flags, void *info, void *user)
104 {
105 tickit_term_input_readable(t->term);
106
107 on_term_timeout(t, TICKIT_EV_FIRE, NULL, NULL);
108 return 0;
109 }
110
111 static Tickit *signal_observer;
112
sighandler(int signum)113 static void sighandler(int signum)
114 {
115 if(signal_observer) {
116 sigaddset(&signal_observer->signal.pending, signum);
117 write(signal_observer->signal.pipefds[1], "\0", 1);
118 }
119 }
120
on_sigpipe_readable(Tickit * t,TickitEventFlags flags,void * info,void * user)121 static int on_sigpipe_readable(Tickit *t, TickitEventFlags flags, void *info, void *user)
122 {
123 char buf[1];
124 read(t->signal.pipefds[0], &buf, 1);
125
126 sigset_t pending;
127 {
128 sigset_t orig;
129 sigprocmask(SIG_BLOCK, &t->signal.watched, &orig);
130
131 pending = t->signal.pending;
132 sigemptyset(&t->signal.pending);
133
134 sigprocmask(SIG_SETMASK, &orig, NULL);
135 }
136
137 TickitWatch *this;
138 for(this = t->signals; this; this = this->next) {
139 if(sigismember(&pending, this->signal.signum))
140 (*this->fn)(this->t, TICKIT_EV_FIRE, NULL, this->user);
141 }
142
143 return 0;
144 }
145
on_sigwinch(Tickit * t,TickitEventFlags flags,void * info,void * user)146 static int on_sigwinch(Tickit *t, TickitEventFlags flags, void *info, void *user)
147 {
148 if(!t->term)
149 return 0;
150
151 tickit_term_refresh_size(t->term);
152 return 0;
153 }
154
setupterm(Tickit * t)155 static void setupterm(Tickit *t)
156 {
157 TickitTerm *tt = t->term;
158
159 tickit_term_await_started_msec(tt, 50);
160
161 if(t->use_altscreen)
162 tickit_term_setctl_int(tt, TICKIT_TERMCTL_ALTSCREEN, 1);
163 tickit_term_setctl_int(tt, TICKIT_TERMCTL_CURSORVIS, 0);
164 tickit_term_setctl_int(tt, TICKIT_TERMCTL_MOUSE, TICKIT_TERM_MOUSEMODE_DRAG);
165 tickit_term_setctl_int(tt, TICKIT_TERMCTL_KEYPAD_APP, 1);
166
167 tickit_term_clear(tt);
168 tickit_term_flush(tt);
169
170 t->done_setup = true;
171 }
172
teardownterm(Tickit * t)173 static void teardownterm(Tickit *t)
174 {
175 t->done_setup = false;
176 }
177
tickit_build(const struct TickitBuilder * builder)178 Tickit *tickit_build(const struct TickitBuilder *builder)
179 {
180 Tickit *t = malloc(sizeof(Tickit));
181 if(!t)
182 return NULL;
183
184 t->refcount = 1;
185
186 t->term = NULL;
187 t->rootwin = NULL;
188
189 t->evhooks = builder->evhooks;
190 if(!t->evhooks)
191 t->evhooks = &tickit_evloop_default;
192 t->evdata = (*t->evhooks->init)(t, builder->evinitdata);
193 if(!t->evdata)
194 goto abort;
195
196 t->iowatches = NULL;
197 t->timers = NULL;
198 t->laters = NULL;
199 t->signals = NULL;
200 t->processes = NULL;
201
202 t->signal.pipefds[0] = -1;
203 t->signal.pipewatch = NULL;
204 sigemptyset(&t->signal.watched);
205 sigemptyset(&t->signal.pending);
206
207 t->sigchldwatch = NULL;
208
209 t->done_setup = false;
210
211 t->use_altscreen = true;
212
213 TickitTerm *tt = builder->tt;
214 if(!tt) {
215 struct TickitTermBuilder term_builder = builder->term_builder;
216 if(!term_builder.output_buffersize)
217 term_builder.output_buffersize = 4096;
218
219 tt = tickit_term_build(&term_builder);
220 if(!tt)
221 goto abort;
222 }
223 t->term = tt; /* take ownership */
224
225 tickit_watch_io(t, tickit_term_get_input_fd(tt), TICKIT_IO_IN,
226 0, on_term_readable, NULL);
227
228 tickit_watch_signal(t, SIGWINCH, 0, on_sigwinch, NULL);
229
230 return t;
231
232 abort:
233 free(t);
234 return NULL;
235 }
236
tickit_new_with_evloop(TickitTerm * tt,TickitEventHooks * evhooks,void * initdata)237 Tickit *tickit_new_with_evloop(TickitTerm *tt, TickitEventHooks *evhooks, void *initdata)
238 {
239 return tickit_build(&(struct TickitBuilder){
240 .tt = tt,
241 /* In case tt==NULL */
242 .term_builder.open = TICKIT_OPEN_STDTTY,
243
244 .evhooks = evhooks,
245 .evinitdata = initdata,
246 });
247 }
248
tickit_new_for_term(TickitTerm * tt)249 Tickit *tickit_new_for_term(TickitTerm *tt)
250 {
251 return tickit_build(&(struct TickitBuilder){
252 .tt = tt,
253 });
254 }
255
tickit_new_stdio(void)256 Tickit *tickit_new_stdio(void)
257 {
258 return tickit_build(&(const struct TickitBuilder){
259 .term_builder.open = TICKIT_OPEN_STDIO,
260 });
261 }
262
tickit_new_stdtty(void)263 Tickit *tickit_new_stdtty(void)
264 {
265 return tickit_build(&(const struct TickitBuilder){
266 .term_builder.open = TICKIT_OPEN_STDTTY,
267 });
268 }
269
insert_watch(TickitWatch ** watchesptr,TickitBindFlags flags,TickitWatch * new)270 static void insert_watch(TickitWatch **watchesptr, TickitBindFlags flags, TickitWatch *new)
271 {
272 if(!(flags & TICKIT_BIND_FIRST)) {
273 while(*watchesptr)
274 watchesptr = &(*watchesptr)->next;
275 }
276
277 new->next = *watchesptr;
278 *watchesptr = new;
279 }
280
destroy_watchlist(Tickit * t,TickitWatch * watches,void (* cancelfunc)(void * data,TickitWatch * watch))281 static void destroy_watchlist(Tickit *t, TickitWatch *watches, void (*cancelfunc)(void *data, TickitWatch *watch))
282 {
283 TickitWatch *this, *next;
284 for(this = watches; this; this = next) {
285 next = this->next;
286
287 if(this->flags & (TICKIT_BIND_UNBIND|TICKIT_BIND_DESTROY))
288 (*this->fn)(this->t, TICKIT_EV_UNBIND|TICKIT_EV_DESTROY, NULL, this->user);
289
290 if(cancelfunc)
291 (*cancelfunc)(t->evdata, this);
292
293 free(this);
294 }
295 }
296
invoke_watch(TickitWatch * watch,TickitEventFlags flags,void * info)297 static void invoke_watch(TickitWatch *watch, TickitEventFlags flags, void *info)
298 {
299 (*watch->fn)(watch->t, flags, info, watch->user);
300
301 /* Remove oneshot watches from the list */
302 TickitWatch **prevp;
303 switch(watch->type) {
304 case WATCH_NONE:
305 case WATCH_IO:
306 case WATCH_SIGNAL:
307 return;
308
309 case WATCH_TIMER:
310 prevp = &watch->t->timers;
311 break;
312
313 case WATCH_LATER:
314 prevp = &watch->t->laters;
315 break;
316
317 case WATCH_PROCESS:
318 prevp = &watch->t->processes;
319 break;
320 }
321
322 while(*prevp) {
323 if(*prevp == watch) {
324 *prevp = watch->next;
325 watch->next = NULL;
326 watch->type = WATCH_NONE;
327 free(watch);
328 return;
329 }
330
331 prevp = &(*prevp)->next;
332 }
333 }
334
tickit_destroy(Tickit * t)335 static void tickit_destroy(Tickit *t)
336 {
337 if(t->done_setup)
338 teardownterm(t);
339
340 if(t->rootwin)
341 tickit_window_unref(t->rootwin);
342 if(t->term) {
343 tickit_term_teardown(t->term);
344 tickit_term_unref(t->term);
345 }
346
347 if(t->sigchldwatch)
348 tickit_watch_cancel(t, t->sigchldwatch);
349
350 if(t->signal.pipewatch)
351 tickit_watch_cancel(t, t->signal.pipewatch);
352 for(int signum = 1; signum < NSIG; signum++) {
353 if(sigismember(&t->signal.watched, signum))
354 sigaction(signum, &(struct sigaction){ .sa_handler = SIG_DFL }, NULL);
355 }
356 if(signal_observer == t)
357 signal_observer = NULL;
358
359 if(t->iowatches)
360 destroy_watchlist(t, t->iowatches, t->evhooks->cancel_io);
361 if(t->timers)
362 destroy_watchlist(t, t->timers, t->evhooks->cancel_timer);
363 if(t->laters)
364 destroy_watchlist(t, t->laters, t->evhooks->cancel_later);
365 if(t->signals)
366 destroy_watchlist(t, t->signals, t->evhooks->cancel_signal);
367 if(t->processes)
368 destroy_watchlist(t, t->processes, t->evhooks->cancel_process);
369
370 (*t->evhooks->destroy)(t->evdata);
371
372 free(t);
373 }
374
tickit_ref(Tickit * t)375 Tickit *tickit_ref(Tickit *t)
376 {
377 t->refcount++;
378 return t;
379 }
380
tickit_unref(Tickit * t)381 void tickit_unref(Tickit *t)
382 {
383 t->refcount--;
384 if(!t->refcount)
385 tickit_destroy(t);
386 }
387
tickit_get_term(Tickit * t)388 TickitTerm *tickit_get_term(Tickit *t)
389 {
390 return t->term;
391 }
392
tickit_get_rootwin(Tickit * t)393 TickitWindow *tickit_get_rootwin(Tickit *t)
394 {
395 if(!t->rootwin) {
396 t->rootwin = tickit_window_new_root2(t, t->term);
397 }
398
399 return t->rootwin;
400 }
401
tickit_getctl_int(Tickit * t,TickitCtl ctl,int * value)402 bool tickit_getctl_int(Tickit *t, TickitCtl ctl, int *value)
403 {
404 switch(ctl) {
405 case TICKIT_CTL_USE_ALTSCREEN:
406 *value = t->use_altscreen;
407 return true;
408
409 case TICKIT_N_CTLS:
410 ;
411 }
412 return false;
413 }
414
tickit_setctl_int(Tickit * t,TickitCtl ctl,int value)415 bool tickit_setctl_int(Tickit *t, TickitCtl ctl, int value)
416 {
417 switch(ctl) {
418 case TICKIT_CTL_USE_ALTSCREEN:
419 t->use_altscreen = value;
420 return true;
421
422 case TICKIT_N_CTLS:
423 ;
424 }
425 return false;
426 }
427
on_sigint(Tickit * t,TickitEventFlags flags,void * info,void * data)428 static int on_sigint(Tickit *t, TickitEventFlags flags, void *info, void *data)
429 {
430 tickit_stop(t);
431 return 0;
432 }
433
tickit_tick(Tickit * t,TickitRunFlags flags)434 void tickit_tick(Tickit *t, TickitRunFlags flags)
435 {
436 if(!t->done_setup && !(flags & TICKIT_RUN_NOSETUP))
437 setupterm(t);
438
439 (*t->evhooks->run)(t->evdata, TICKIT_RUN_ONCE | flags);
440 }
441
tickit_run(Tickit * t)442 void tickit_run(Tickit *t)
443 {
444 void *sigint_watch = tickit_watch_signal(t, SIGINT, 0, &on_sigint, NULL);
445
446 if(!t->done_setup)
447 setupterm(t);
448
449 (*t->evhooks->run)(t->evdata, TICKIT_RUN_DEFAULT);
450
451 tickit_watch_cancel(t, sigint_watch);
452 }
453
tickit_stop(Tickit * t)454 void tickit_stop(Tickit *t)
455 {
456 return (*t->evhooks->stop)(t->evdata);
457 }
458
tickit_watch_io(Tickit * t,int fd,TickitIOCondition cond,TickitBindFlags flags,TickitCallbackFn * fn,void * user)459 void *tickit_watch_io(Tickit *t, int fd, TickitIOCondition cond, TickitBindFlags flags, TickitCallbackFn *fn, void *user)
460 {
461 TickitWatch *watch = malloc(sizeof(TickitWatch));
462 if(!watch)
463 return NULL;
464
465 watch->next = NULL;
466 watch->t = t;
467 watch->type = WATCH_IO;
468
469 watch->flags = flags & (TICKIT_BIND_UNBIND|TICKIT_BIND_UNBIND);
470 watch->fn = fn;
471 watch->user = user;
472
473 watch->io.fd = fd;
474 watch->io.cond = cond;
475
476 if(!(*t->evhooks->io)(t->evdata, fd, cond, flags, watch))
477 goto fail;
478
479 insert_watch(&t->iowatches, flags, watch);
480
481 return watch;
482
483 fail:
484 free(watch);
485 return NULL;
486 }
487
tickit_watch_io_read(Tickit * t,int fd,TickitBindFlags flags,TickitCallbackFn * fn,void * user)488 void *tickit_watch_io_read(Tickit *t, int fd, TickitBindFlags flags, TickitCallbackFn *fn, void *user)
489 {
490 return tickit_watch_io(t, fd, TICKIT_IO_IN, flags, fn, user);
491 }
492
tickit_watch_timer_at_tv(Tickit * t,const struct timeval * at,TickitBindFlags flags,TickitCallbackFn * fn,void * user)493 void *tickit_watch_timer_at_tv(Tickit *t, const struct timeval *at, TickitBindFlags flags, TickitCallbackFn *fn, void *user)
494 {
495 TickitWatch *watch = malloc(sizeof(TickitWatch));
496 if(!watch)
497 return NULL;
498
499 watch->next = NULL;
500 watch->t = t;
501 watch->type = WATCH_TIMER;
502
503 watch->flags = flags & (TICKIT_BIND_UNBIND|TICKIT_BIND_DESTROY);
504 watch->fn = fn;
505 watch->user = user;
506
507 watch->timer.at = *at;
508
509 if(t->evhooks->timer)
510 if(!(*t->evhooks->timer)(t->evdata, at, flags, watch))
511 goto fail;
512
513 TickitWatch **prevp = &t->timers;
514 /* Try to insert in-order at matching timestamp; don't use insert_watch() */
515 while(*prevp && !timercmp(&(*prevp)->timer.at, at, >))
516 prevp = &(*prevp)->next;
517
518 watch->next = *prevp;
519 *prevp = watch;
520
521 return watch;
522
523 fail:
524 free(watch);
525 return NULL;
526 }
527
tickit_watch_timer_at_epoch(Tickit * t,time_t at,TickitBindFlags flags,TickitCallbackFn * func,void * user)528 void *tickit_watch_timer_at_epoch(Tickit *t, time_t at, TickitBindFlags flags, TickitCallbackFn *func, void *user)
529 {
530 return tickit_watch_timer_at_tv(t, &(struct timeval){
531 .tv_sec = at,
532 .tv_usec = 0,
533 }, flags, func, user);
534 }
535
tickit_watch_timer_after_tv(Tickit * t,const struct timeval * after,TickitBindFlags flags,TickitCallbackFn * fn,void * user)536 void *tickit_watch_timer_after_tv(Tickit *t, const struct timeval *after, TickitBindFlags flags, TickitCallbackFn *fn, void *user)
537 {
538 struct timeval at;
539 gettimeofday(&at, NULL);
540
541 /* at + after ==> at */
542 timeradd(&at, after, &at);
543
544 return tickit_watch_timer_at_tv(t, &at, flags, fn, user);
545 }
546
tickit_watch_timer_after_msec(Tickit * t,int msec,TickitBindFlags flags,TickitCallbackFn * fn,void * user)547 void *tickit_watch_timer_after_msec(Tickit *t, int msec, TickitBindFlags flags, TickitCallbackFn *fn, void *user)
548 {
549 return tickit_watch_timer_after_tv(t, &(struct timeval){
550 .tv_sec = msec / 1000,
551 .tv_usec = (msec % 1000) * 1000,
552 }, flags, fn, user);
553 }
554
tickit_watch_later(Tickit * t,TickitBindFlags flags,TickitCallbackFn * fn,void * user)555 void *tickit_watch_later(Tickit *t, TickitBindFlags flags, TickitCallbackFn *fn, void *user)
556 {
557 TickitWatch *watch = malloc(sizeof(TickitWatch));
558 if(!watch)
559 return NULL;
560
561 watch->next = NULL;
562 watch->t = t;
563 watch->type = WATCH_LATER;
564
565 watch->flags = flags & (TICKIT_BIND_UNBIND|TICKIT_BIND_DESTROY);
566 watch->fn = fn;
567 watch->user = user;
568
569 if(t->evhooks->later)
570 if(!(*t->evhooks->later)(t->evdata, flags, watch))
571 goto fail;
572
573 insert_watch(&t->laters, flags, watch);
574
575 return watch;
576
577 fail:
578 free(watch);
579 return NULL;
580 }
581
watch_signal(Tickit * t,int signum,TickitWatch * watch)582 static void watch_signal(Tickit *t, int signum, TickitWatch *watch)
583 {
584 if(t->signal.pipefds[0] == -1) {
585 pipe(t->signal.pipefds);
586 t->signal.pipewatch = tickit_watch_io(t, t->signal.pipefds[0], TICKIT_IO_IN, 0, &on_sigpipe_readable, t);
587 }
588
589 if(sigismember(&t->signal.watched, signum))
590 return;
591
592 sigaction(signum, &(struct sigaction){ .sa_handler = sighandler }, NULL);
593 sigaddset(&t->signal.watched, signum);
594
595 if(!signal_observer)
596 signal_observer = t;
597 }
598
unwatch_signal(Tickit * t,TickitWatch * watch)599 static void unwatch_signal(Tickit *t, TickitWatch *watch)
600 {
601 int signum = watch->signal.signum;
602
603 /* watch has already been removed from t->signals */
604 TickitWatch *this;
605 for(this = t->signals; this; this = this->next) {
606 if(this->signal.signum == signum)
607 return;
608 }
609
610 sigdelset(&t->signal.watched, signum);
611 sigaction(signum, &(struct sigaction){ .sa_handler = SIG_DFL }, NULL);
612 }
613
tickit_watch_signal(Tickit * t,int signum,TickitBindFlags flags,TickitCallbackFn * fn,void * user)614 void *tickit_watch_signal(Tickit *t, int signum, TickitBindFlags flags, TickitCallbackFn *fn, void *user)
615 {
616 TickitWatch *watch = malloc(sizeof(TickitWatch));
617 if(!watch)
618 return NULL;
619
620 watch->next = NULL;
621 watch->t = t;
622 watch->type = WATCH_SIGNAL;
623
624 watch->flags = flags & (TICKIT_BIND_UNBIND|TICKIT_BIND_DESTROY);
625 watch->fn = fn;
626 watch->user = user;
627
628 watch->signal.signum = signum;
629
630 if(!t->evhooks->signal ||
631 !(*t->evhooks->signal)(t->evdata, signum, flags, watch))
632 watch_signal(t, signum, watch);
633
634 insert_watch(&t->signals, flags, watch);
635
636 return watch;
637 }
638
on_sigchld(Tickit * t,TickitEventFlags flags,void * info,void * data)639 static int on_sigchld(Tickit *t, TickitEventFlags flags, void *info, void *data)
640 {
641 TickitWatch *this, *next;
642 for(this = t->processes; this; this = next) {
643 next = this->next;
644
645 TickitProcessWatchInfo info;
646 if(waitpid(this->process.pid, &info.wstatus, WNOHANG) <= 0)
647 continue;
648
649 info.pid = this->process.pid;
650 invoke_watch(this, TICKIT_EV_FIRE, &info);
651 }
652 return 0;
653 }
654
process_notify(Tickit * t,TickitEventFlags flags,void * _info,void * data)655 static int process_notify(Tickit *t, TickitEventFlags flags, void *_info, void *data)
656 {
657 TickitWatch *watch = data;
658
659 tickit_evloop_invoke_processwatch(watch, TICKIT_EV_FIRE, watch->process.wstatus);
660
661 return 0;
662 }
663
tickit_watch_process(Tickit * t,pid_t pid,TickitBindFlags flags,TickitCallbackFn * fn,void * user)664 void *tickit_watch_process(Tickit *t, pid_t pid, TickitBindFlags flags, TickitCallbackFn *fn, void *user)
665 {
666 TickitWatch *watch = malloc(sizeof(TickitWatch));
667 if(!watch)
668 return NULL;
669
670 watch->next = NULL;
671 watch->t = t;
672 watch->type = WATCH_PROCESS;
673
674 watch->flags = flags & (TICKIT_BIND_UNBIND|TICKIT_BIND_DESTROY);
675 watch->fn = fn;
676 watch->user = user;
677
678 watch->process.pid = pid;
679
680 if(!t->evhooks->process ||
681 !(*t->evhooks->process)(t->evdata, pid, flags, watch)) {
682 if(!t->sigchldwatch)
683 t->sigchldwatch = tickit_watch_signal(t, SIGCHLD, 0, &on_sigchld, NULL);
684
685 if(waitpid(pid, &watch->process.wstatus, WNOHANG) > 0) {
686 /* Process already exited, so SIGCHLD won't see it. We can't invoke
687 * callback immediately as user will be expecting it to only be called via
688 * tickit_run(). We'll install a later handler for it
689 */
690 tickit_watch_later(t, 0, process_notify, watch);
691
692 return watch;
693 }
694 }
695
696 insert_watch(&t->processes, flags, watch);
697
698 return watch;
699 }
700
tickit_watch_cancel(Tickit * t,void * _watch)701 void tickit_watch_cancel(Tickit *t, void *_watch)
702 {
703 TickitWatch *watch = _watch;
704
705 TickitWatch **thisp;
706 switch(watch->type) {
707 case WATCH_IO:
708 thisp = &t->iowatches;
709 break;
710 case WATCH_TIMER:
711 thisp = &t->timers;
712 break;
713 case WATCH_LATER:
714 thisp = &t->laters;
715 break;
716 case WATCH_SIGNAL:
717 thisp = &t->signals;
718 break;
719 case WATCH_PROCESS:
720 thisp = &t->processes;
721 break;
722
723 case WATCH_NONE:
724 return;
725 }
726
727 while(*thisp) {
728 TickitWatch *this = *thisp;
729 if(this == watch) {
730 *thisp = this->next;
731
732 if(this->flags & TICKIT_BIND_UNBIND)
733 (*this->fn)(t, TICKIT_EV_UNBIND, NULL, this->user);
734
735 switch(this->type) {
736 case WATCH_IO:
737 (*t->evhooks->cancel_io)(t->evdata, this);
738 break;
739 case WATCH_TIMER:
740 if(t->evhooks->cancel_timer)
741 (*t->evhooks->cancel_timer)(t->evdata, this);
742 break;
743 case WATCH_LATER:
744 if(t->evhooks->cancel_later)
745 (*t->evhooks->cancel_later)(t->evdata, this);
746 break;
747 case WATCH_SIGNAL:
748 if(t->evhooks->cancel_signal)
749 (*t->evhooks->cancel_signal)(t->evdata, this);
750 else
751 unwatch_signal(t, this);
752 break;
753 case WATCH_PROCESS:
754 if(t->evhooks->cancel_process)
755 (*t->evhooks->cancel_process)(t->evdata, this);
756 break;
757
758 case WATCH_NONE:
759 ;
760 }
761
762 free(this);
763 }
764
765 if(!thisp || !*thisp)
766 break;
767
768 thisp = &(*thisp)->next;
769 }
770 }
771
tickit_evloop_next_timer_msec(Tickit * t)772 int tickit_evloop_next_timer_msec(Tickit *t)
773 {
774 if(t->laters)
775 return 0;
776
777 if(!t->timers)
778 return -1;
779
780 struct timeval now, delay;
781 gettimeofday(&now, NULL);
782
783 /* timers->timer.at - now ==> delay */
784 timersub(&t->timers->timer.at, &now, &delay);
785
786 int msec = (delay.tv_sec * 1000) + (delay.tv_usec / 1000);
787 if(msec < 0)
788 msec = 0;
789
790 return msec;
791 }
792
tickit_evloop_invoke_timers(Tickit * t)793 void tickit_evloop_invoke_timers(Tickit *t)
794 {
795 /* detach the later queue before running any events */
796 TickitWatch *later = t->laters;
797 t->laters = NULL;
798
799 if(t->timers) {
800 struct timeval now;
801 gettimeofday(&now, NULL);
802
803 /* timer queue is stored ordered, so we can just eat a prefix
804 * of it
805 */
806
807 TickitWatch *this = t->timers;
808 while(this) {
809 if(timercmp(&this->timer.at, &now, >))
810 break;
811
812 /* TODO: consider what info might point at */
813 (*this->fn)(this->t, TICKIT_EV_FIRE|TICKIT_EV_UNBIND, NULL, this->user);
814
815 TickitWatch *next = this->next;
816 free(this);
817 this = next;
818 }
819
820 t->timers = this;
821 }
822
823 while(later) {
824 (*later->fn)(later->t, TICKIT_EV_FIRE|TICKIT_EV_UNBIND, NULL, later->user);
825
826 TickitWatch *next = later->next;
827 free(later);
828 later = next;
829 }
830 }
831
tickit_evloop_get_watch_data(TickitWatch * watch)832 void *tickit_evloop_get_watch_data(TickitWatch *watch)
833 {
834 return watch->evdata.ptr;
835 }
836
tickit_evloop_set_watch_data(TickitWatch * watch,void * data)837 void tickit_evloop_set_watch_data(TickitWatch *watch, void *data)
838 {
839 watch->evdata.ptr = data;
840 }
841
tickit_evloop_get_watch_data_int(TickitWatch * watch)842 int tickit_evloop_get_watch_data_int(TickitWatch *watch)
843 {
844 return watch->evdata.i;
845 }
846
tickit_evloop_set_watch_data_int(TickitWatch * watch,int data)847 void tickit_evloop_set_watch_data_int(TickitWatch *watch, int data)
848 {
849 watch->evdata.i = data;
850 }
851
tickit_evloop_invoke_watch(TickitWatch * watch,TickitEventFlags flags)852 void tickit_evloop_invoke_watch(TickitWatch *watch, TickitEventFlags flags)
853 {
854 invoke_watch(watch, flags, NULL);
855 }
856
tickit_evloop_invoke_iowatch(TickitWatch * watch,TickitEventFlags flags,TickitIOCondition cond)857 void tickit_evloop_invoke_iowatch(TickitWatch *watch, TickitEventFlags flags, TickitIOCondition cond)
858 {
859 invoke_watch(watch, flags, &(TickitIOWatchInfo){
860 .fd = watch->io.fd,
861 .cond = cond,
862 });
863 }
864
tickit_evloop_invoke_processwatch(TickitWatch * watch,TickitEventFlags flags,int wstatus)865 void tickit_evloop_invoke_processwatch(TickitWatch *watch, TickitEventFlags flags, int wstatus)
866 {
867 invoke_watch(watch, flags, &(TickitProcessWatchInfo){
868 .pid = watch->process.pid,
869 .wstatus = wstatus,
870 });
871 }
872
tickit_evloop_invoke_sigwatches(Tickit * t,int signum)873 void tickit_evloop_invoke_sigwatches(Tickit *t, int signum)
874 {
875 TickitWatch *this;
876 for(this = t->signals; this; this = this->next) {
877 if(this->signal.signum == signum)
878 (*this->fn)(this->t, TICKIT_EV_FIRE, NULL, this->user);
879 }
880 }
881
tickit_evloop_sigwinch(Tickit * t)882 void tickit_evloop_sigwinch(Tickit *t)
883 {
884 /* No longer need to do anything, as on_sigwinch() has already handled it
885 * But this function needs to remain for ABI reasons
886 */
887 }
888
tickit_ctlname(TickitCtl ctl)889 const char *tickit_ctlname(TickitCtl ctl)
890 {
891 switch(ctl) {
892 case TICKIT_CTL_USE_ALTSCREEN: return "use-altscreen";
893
894 case TICKIT_N_CTLS: ;
895 }
896 return NULL;
897 }
898
tickit_lookup_ctl(const char * name)899 TickitCtl tickit_lookup_ctl(const char *name)
900 {
901 const char *s;
902
903 for(TickitCtl ctl = 1; ctl < TICKIT_N_CTLS; ctl++)
904 if((s = tickit_ctlname(ctl)) && streq(name, s))
905 return ctl;
906
907 return -1;
908 }
909
tickit_ctltype(TickitCtl ctl)910 TickitType tickit_ctltype(TickitCtl ctl)
911 {
912 switch(ctl) {
913 case TICKIT_CTL_USE_ALTSCREEN:
914 return TICKIT_TYPE_BOOL;
915
916 case TICKIT_N_CTLS:
917 ;
918 }
919 return TICKIT_TYPE_NONE;
920 }
921
tickit_version_major(void)922 int tickit_version_major(void) { return TICKIT_VERSION_MAJOR; }
tickit_version_minor(void)923 int tickit_version_minor(void) { return TICKIT_VERSION_MINOR; }
tickit_version_patch(void)924 int tickit_version_patch(void) { return TICKIT_VERSION_PATCH; }
925