1 /*  You may distribute under the terms of either the GNU General Public License
2  *  or the Artistic License (the same terms as Perl itself)
3  *
4  *  (C) Paul Evans, 2011-2021 -- leonerd@leonerd.org.uk
5  */
6 #define PERL_NO_GET_CONTEXT
7 
8 #include "EXTERN.h"
9 #include "perl.h"
10 #include "XSUB.h"
11 
12 #include <tickit.h>
13 #include <tickit-evloop.h>
14 #include <tickit-termdrv.h>
15 #include <tickit-mockterm.h>
16 
17 #define streq(a,b) (strcmp(a,b)==0)
18 
19 // UVs also have the IOK flag set
20 #define SvIsNumeric(sv) (SvFLAGS(sv) & (SVp_IOK|SVp_NOK))
21 
22 // back-compat for 5.10.0 since it's easy
23 #ifndef mPUSHs
24 #  define mPUSHs(sv)  PUSHs(sv_2mortal(sv))
25 #endif
26 
27 #ifndef newSVivpv
28 #  define newSVivpv(i,p)  S_newSVivpv(aTHX_ i, p)
S_newSVivpv(pTHX_ int iv,const char * pv)29 static SV *S_newSVivpv(pTHX_ int iv, const char *pv)
30 {
31   SV *sv = newSViv(iv);
32   if(pv) { sv_setpv(sv, pv); SvIOK_on(sv); }
33   return sv;
34 }
35 #endif
36 
37 // A handy helper for setting PL_curcop
38 #define SET_PL_curcop                          \
39   do {                                         \
40     static COP *cop;                           \
41     if(!cop) {                                 \
42       SAVEVPTR(PL_parser);                     \
43       Newxz(PL_parser, 1, yy_parser);          \
44       SAVEFREEPV(PL_parser);                   \
45                                                \
46       cop = (COP *)newSTATEOP(0, NULL, NULL);  \
47       CopFILE_set(cop, __FILE__);              \
48       CopLINE_set(cop, __LINE__);              \
49     }                                          \
50     PL_curcop = cop;                           \
51   } while(0)
52 
53 #define cv_from_sv(sv, name)  S_cv_from_sv(aTHX_ sv, name)
S_cv_from_sv(pTHX_ SV * sv,const char * name)54 static CV *S_cv_from_sv(pTHX_ SV *sv, const char *name)
55 {
56   HV *hv;
57   GV *gvp;
58   SvGETMAGIC(sv);
59   CV *cv = sv_2cv(sv, &hv, &gvp, 0);
60   if(!cv)
61     croak("Expected CODE reference for %s", name);
62   return cv;
63 }
64 
65 #define tickit_focusevtype2sv(type)  S_tickit_focusevtype2sv(aTHX_ type)
S_tickit_focusevtype2sv(pTHX_ TickitFocusEventType type)66 static SV *S_tickit_focusevtype2sv(pTHX_ TickitFocusEventType type)
67 {
68   const char *name = NULL;
69   switch(type) {
70     case TICKIT_FOCUSEV_IN:  name = "in";  break;
71     case TICKIT_FOCUSEV_OUT: name = "out"; break;
72   }
73   return newSVivpv(type, name);
74 }
75 
76 #define tickit_name2focusev(name)  S_tickit_name2focusev(aTHX_ name)
S_tickit_name2focusev(pTHX_ const char * name)77 static TickitFocusEventType S_tickit_name2focusev(pTHX_ const char *name)
78 {
79   switch(name[0]) {
80     case 'i':
81       return streq(name+1, "n") ? TICKIT_FOCUSEV_IN
82                                 : -1;
83     case 'o':
84       return streq(name+1, "ut") ? TICKIT_FOCUSEV_OUT
85                                  : -1;
86   }
87   return -1;
88 }
89 
90 #define tickit_keyevtype2sv(type)  S_tickit_keyevtype2sv(aTHX_ type)
S_tickit_keyevtype2sv(pTHX_ int type)91 static SV *S_tickit_keyevtype2sv(pTHX_ int type)
92 {
93   const char *name = NULL;
94   switch(type) {
95     case TICKIT_KEYEV_KEY:  name = "key";  break;
96     case TICKIT_KEYEV_TEXT: name = "text"; break;
97   }
98   return newSVivpv(type, name);
99 }
100 
101 #define tickit_name2keyev(name)  S_tickit_name2keyev(aTHX_ name)
S_tickit_name2keyev(pTHX_ const char * name)102 static TickitKeyEventType S_tickit_name2keyev(pTHX_ const char *name)
103 {
104   switch(name[0]) {
105     case 'k':
106       return streq(name+1, "ey") ? TICKIT_KEYEV_KEY
107                                  : -1;
108     case 't':
109       return streq(name+1, "ext") ? TICKIT_KEYEV_TEXT
110                                   : -1;
111   }
112   return -1;
113 }
114 
115 #define tickit_mouseevtype2sv(type)  S_tickit_mouseevtype2sv(aTHX_ type)
S_tickit_mouseevtype2sv(pTHX_ int type)116 static SV *S_tickit_mouseevtype2sv(pTHX_ int type)
117 {
118   const char *name = NULL;
119   switch(type) {
120     case TICKIT_MOUSEEV_PRESS:   name = "press";   break;
121     case TICKIT_MOUSEEV_DRAG:    name = "drag";    break;
122     case TICKIT_MOUSEEV_RELEASE: name = "release"; break;
123     case TICKIT_MOUSEEV_WHEEL:   name = "wheel";   break;
124 
125     case TICKIT_MOUSEEV_DRAG_START:   name = "drag_start";   break;
126     case TICKIT_MOUSEEV_DRAG_DROP:    name = "drag_drop";    break;
127     case TICKIT_MOUSEEV_DRAG_STOP:    name = "drag_stop";    break;
128     case TICKIT_MOUSEEV_DRAG_OUTSIDE: name = "drag_outside"; break;
129   }
130   return newSVivpv(type, name);
131 }
132 
133 #define tickit_name2mouseev(name)  S_tickit_name2mouseev(aTHX_ name)
S_tickit_name2mouseev(pTHX_ const char * name)134 static TickitMouseEventType S_tickit_name2mouseev(pTHX_ const char *name)
135 {
136   switch(name[0]) {
137     case 'd':
138       return streq(name+1, "rag")         ? TICKIT_MOUSEEV_DRAG
139            : streq(name+1, "rag_start")   ? TICKIT_MOUSEEV_DRAG_START
140            : streq(name+1, "rag_drop")    ? TICKIT_MOUSEEV_DRAG_DROP
141            : streq(name+1, "rag_stop")    ? TICKIT_MOUSEEV_DRAG_STOP
142            : streq(name+1, "rag_outside") ? TICKIT_MOUSEEV_DRAG_OUTSIDE
143                                           : -1;
144     case 'p':
145       return streq(name+1, "ress") ? TICKIT_MOUSEEV_PRESS
146                                    : -1;
147     case 'r':
148       return streq(name+1, "elease") ? TICKIT_MOUSEEV_RELEASE
149                                      : -1;
150     case 'w':
151       return streq(name+1, "heel") ? TICKIT_MOUSEEV_WHEEL
152                                    : -1;
153   }
154   return -1;
155 }
156 
157 #define tickit_mouseevbutton2sv(type, button)  S_tickit_mouseevbutton2sv(aTHX_ type, button)
S_tickit_mouseevbutton2sv(pTHX_ int type,int button)158 static SV *S_tickit_mouseevbutton2sv(pTHX_ int type, int button)
159 {
160   const char *name = NULL;
161   if(type == TICKIT_MOUSEEV_WHEEL)
162     switch(button) {
163       case TICKIT_MOUSEWHEEL_UP:   name = "up";   break;
164       case TICKIT_MOUSEWHEEL_DOWN: name = "down"; break;
165     }
166   return newSVivpv(button, name);
167 }
168 
169 #define tickit_name2mousewheel(name)  S_tickit_name2mousewheel(aTHX_ name)
S_tickit_name2mousewheel(pTHX_ const char * name)170 static int S_tickit_name2mousewheel(pTHX_ const char *name)
171 {
172   switch(name[0]) {
173     case 'd':
174       return streq(name+1, "own") ? TICKIT_MOUSEWHEEL_DOWN
175                                   : -1;
176     case 'u':
177       return streq(name+1, "p") ? TICKIT_MOUSEWHEEL_UP
178                                 : -1;
179   }
180   return -1;
181 }
182 
183 struct GenericEventData
184 {
185 #ifdef tTHX
186   tTHX myperl;
187 #endif
188   int ev;
189   SV *self;  // only for window bindings; unused for term
190   CV *code;
191   SV *data;
192 };
193 
194 #define new_eventdata(ev, data, code)       S_new_eventdata(aTHX_ ev, data, code)
195 #define new_eventdata_codeonly(code)        S_new_eventdata(aTHX_ 0,  NULL, code)
S_new_eventdata(pTHX_ int ev,SV * data,CV * code)196 static struct GenericEventData *S_new_eventdata(pTHX_ int ev, SV *data, CV *code)
197 {
198   struct GenericEventData *ret;
199   Newx(ret, 1, struct GenericEventData);
200 #ifdef tTHX
201   ret->myperl = aTHX;
202 #endif
203   ret->ev   = ev;
204   ret->data = data;
205   ret->code = code ? (CV *)SvREFCNT_inc(code) : NULL;
206   return ret;
207 }
208 
209 typedef TickitKeyEventInfo   *Tickit__Event__Key;
210 typedef TickitMouseEventInfo *Tickit__Event__Mouse;
211 
212 /***************
213  * Tickit::Pen *
214  ***************/
215 
216 typedef TickitPen *Tickit__Pen;
217 
218 #define newSVpen_noinc(pen, package)  S_newSVpen_noinc(aTHX_ pen, package)
S_newSVpen_noinc(pTHX_ TickitPen * pen,char * package)219 static SV *S_newSVpen_noinc(pTHX_ TickitPen *pen, char *package)
220 {
221   SV *sv = newSV(0);
222   sv_setref_pv(sv, package ? package : "Tickit::Pen::Immutable", pen);
223 
224   return sv;
225 }
226 
227 #define newSVpen(pen, package)  S_newSVpen_noinc(aTHX_ tickit_pen_ref(pen), package)
228 
229 enum {
230   TICKIT_PEN_FG_RGB8 = 0x100 | TICKIT_PEN_FG,
231   TICKIT_PEN_BG_RGB8 = 0x100 | TICKIT_PEN_BG,
232 };
233 
pen_parse_attrname(const char * name)234 static int pen_parse_attrname(const char *name)
235 {
236   const char *end = strchr(name, ':');
237   TickitPenAttr ret;
238 
239   if(!end)
240     return tickit_pen_lookup_attr(name);
241 
242   if(!strEQ(end+1, "rgb8"))
243     return -1;
244 
245   name = strndup(name, end - name);
246   ret = tickit_pen_lookup_attr(name);
247   free((void *)name);
248 
249   switch(ret) {
250     case TICKIT_PEN_FG: return TICKIT_PEN_FG_RGB8;
251     case TICKIT_PEN_BG: return TICKIT_PEN_BG_RGB8;
252     default: return -1;
253   }
254 }
255 
256 #define pen_get_attr(pen, attr)  S_pen_get_attr(aTHX_ pen, attr)
S_pen_get_attr(pTHX_ TickitPen * pen,int attr)257 static SV *S_pen_get_attr(pTHX_ TickitPen *pen, int attr)
258 {
259   switch(attr) {
260     case TICKIT_PEN_FG_RGB8:
261     case TICKIT_PEN_BG_RGB8:
262     {
263       TickitPenRGB8 val;
264       val = tickit_pen_get_colour_attr_rgb8(pen, attr & 0xFF);
265       return newSVpvf("#%02X%02X%02X", val.r, val.g, val.b);
266     }
267   }
268 
269   switch(tickit_pen_attrtype(attr)) {
270   case TICKIT_PENTYPE_BOOL:
271     return tickit_pen_get_bool_attr(pen, attr) ? &PL_sv_yes : &PL_sv_no;
272   case TICKIT_PENTYPE_INT:
273     return newSViv(tickit_pen_get_int_attr(pen, attr));
274   case TICKIT_PENTYPE_COLOUR:
275     return newSViv(tickit_pen_get_colour_attr(pen, attr));
276   }
277 
278   croak("Unreachable: unknown pen type");
279 }
280 
281 #define pen_set_attr(pen, attr, val)  S_pen_set_attr(aTHX_ pen, attr, val)
S_pen_set_attr(pTHX_ TickitPen * pen,int attr,SV * val)282 static void S_pen_set_attr(pTHX_ TickitPen *pen, int attr, SV *val)
283 {
284   switch(attr) {
285     case TICKIT_PEN_FG_RGB8:
286     case TICKIT_PEN_BG_RGB8:
287     {
288       TickitPenRGB8 v;
289       if(sscanf(SvPV_nolen(val), "#%02hhx%02hhx%02hhx", &v.r, &v.g, &v.b) < 3)
290         return;
291       tickit_pen_set_colour_attr_rgb8(pen, attr & 0xFF, v);
292       return;
293     }
294   }
295 
296   switch(tickit_pen_attrtype(attr)) {
297   case TICKIT_PENTYPE_INT:
298     tickit_pen_set_int_attr(pen, attr, SvOK(val) ? SvIV(val) : -1);
299     break;
300   case TICKIT_PENTYPE_BOOL:
301     tickit_pen_set_bool_attr(pen, attr, SvOK(val) ? SvIV(val) : 0);
302     break;
303   case TICKIT_PENTYPE_COLOUR:
304     if(!SvPOK(val) && SvIsNumeric(val))
305       tickit_pen_set_colour_attr(pen, attr, SvIV(val));
306     else if(SvPOK(val))
307       tickit_pen_set_colour_attr_desc(pen, attr, SvPV_nolen(val));
308     else
309       tickit_pen_set_colour_attr(pen, attr, -1);
310     break;
311   }
312 }
313 
314 #define pen_from_args(args, argcount)  S_pen_from_args(aTHX_ args, argcount)
S_pen_from_args(pTHX_ SV ** args,int argcount)315 static TickitPen *S_pen_from_args(pTHX_ SV **args, int argcount)
316 {
317   int i;
318   TickitPen *pen = tickit_pen_new();
319 
320   for(i = 0; i < argcount; i += 2) {
321     const char *name  = SvPV_nolen(args[i]);
322     SV         *value = args[i+1];
323 
324     TickitPenAttr attr = tickit_pen_lookup_attr(name);
325     if(attr != -1)
326       pen_set_attr(pen, attr, value);
327   }
328 
329   return pen;
330 }
331 
332 #define pen_set_attrs(pen, attrs)  S_pen_set_attrs(aTHX_ pen, attrs)
S_pen_set_attrs(pTHX_ TickitPen * pen,HV * attrs)333 static void S_pen_set_attrs(pTHX_ TickitPen *pen, HV *attrs)
334 {
335   TickitPenAttr a;
336   SV *val;
337 
338   for(a = 1; a < TICKIT_N_PEN_ATTRS; a++) {
339     const char *name = tickit_pen_attrname(a);
340     val = hv_delete(attrs, name, strlen(name), 0);
341     if(!val)
342       continue;
343 
344     if(!SvOK(val))
345       tickit_pen_clear_attr(pen, a);
346     else
347       pen_set_attr(pen, a, val);
348   }
349 
350   if((val = hv_delete(attrs, "fg:rgb8", 7, 0))) {
351     if(SvOK(val)) {
352       pen_set_attr(pen, TICKIT_PEN_FG_RGB8, val);
353     }
354     else
355       tickit_pen_set_colour_attr(pen, TICKIT_PEN_FG, tickit_pen_get_colour_attr(pen, TICKIT_PEN_FG));
356   }
357   if((val = hv_delete(attrs, "bg:rgb8", 7, 0))) {
358     if(SvOK(val)) {
359       pen_set_attr(pen, TICKIT_PEN_BG_RGB8, val);
360     }
361     else
362       tickit_pen_set_colour_attr(pen, TICKIT_PEN_BG, tickit_pen_get_colour_attr(pen, TICKIT_PEN_BG));
363   }
364 }
365 
366 /****************
367  * Tickit::Rect *
368  ****************/
369 
370 typedef TickitRect *Tickit__Rect, *Tickit__Rect_MAYBE;
371 
372 /* Really cheating and treading on Perl's namespace but hopefully it will be OK */
373 #define newSVrect(rect)  S_newSVrect(aTHX_ rect)
S_newSVrect(pTHX_ TickitRect * rect)374 static SV *S_newSVrect(pTHX_ TickitRect *rect)
375 {
376   TickitRect *self;
377   Newx(self, 1, TickitRect);
378   *self = *rect;
379   return sv_setref_pv(newSV(0), "Tickit::Rect", self);
380 }
381 #define mPUSHrect(rect) PUSHs(sv_2mortal(newSVrect(rect)))
382 
383 /*******************
384  * Tickit::RectSet *
385  *******************/
386 
387 typedef TickitRectSet *Tickit__RectSet;
388 
389 /************************
390  * Tickit::RenderBuffer *
391  ************************/
392 
393 typedef TickitRenderBuffer *Tickit__RenderBuffer;
394 
395 #define newSVrb_noinc(rb)  S_newSVrb_noinc(aTHX_ rb)
S_newSVrb_noinc(pTHX_ TickitRenderBuffer * rb)396 static SV *S_newSVrb_noinc(pTHX_ TickitRenderBuffer *rb)
397 {
398   SV *sv = newSV(0);
399   sv_setref_pv(sv, "Tickit::RenderBuffer", rb);
400 
401   return sv;
402 }
403 
404 #define newSVrb(rb)  S_newSVrb_noinc(aTHX_ tickit_renderbuffer_ref(rb))
405 
406 /****************
407  * Tickit::Term *
408  ****************/
409 
410 typedef TickitTerm *Tickit__Term, *Tickit__Term_MAYBE;
411 
412 #define newSVterm_noinc(tt, package)  S_newSVterm_noinc(aTHX_ tt, package)
S_newSVterm_noinc(pTHX_ TickitTerm * tt,char * package)413 static SV *S_newSVterm_noinc(pTHX_ TickitTerm *tt, char *package)
414 {
415   SV *sv = newSV(0);
416   sv_setref_pv(sv, package, tt);
417 
418   return sv;
419 }
420 
421 #define newSVterm(tt, package)  S_newSVterm_noinc(aTHX_ tickit_term_ref(tt), package)
422 
term_userevent_fn(TickitTerm * tt,TickitEventFlags flags,void * _info,void * user)423 static int term_userevent_fn(TickitTerm *tt, TickitEventFlags flags, void *_info, void *user)
424 {
425   struct GenericEventData *data = user;
426   dTHXa(data->myperl);
427 
428   SET_PL_curcop;
429 
430   int ret = 0;
431 
432   if(flags & TICKIT_EV_FIRE) {
433     SV *info_sv = newSV(0);
434     char *evname = NULL;
435 
436     switch((TickitTermEvent)data->ev) {
437       case TICKIT_TERM_ON_DESTROY:
438         croak("TICKIT_TERM_ON_DESTROY should not be TICKIT_EV_FIRE'd");
439         break;
440 
441       case TICKIT_TERM_ON_KEY: {
442         TickitKeyEventInfo *info = _info, *self;
443         Newx(self, 1, TickitKeyEventInfo);
444         *self = *info;
445         self->str = savepv(info->str);
446 
447         evname = "key";
448         sv_setref_pv(info_sv, "Tickit::Event::Key", self);
449         break;
450       }
451 
452       case TICKIT_TERM_ON_MOUSE: {
453         TickitMouseEventInfo *info = _info, *self;
454         Newx(self, 1, TickitMouseEventInfo);
455         *self = *info;
456 
457         evname = "mouse";
458         sv_setref_pv(info_sv, "Tickit::Event::Mouse", self);
459         break;
460       }
461 
462       case TICKIT_TERM_ON_RESIZE: {
463         TickitResizeEventInfo *info = _info, *self;
464         Newx(self, 1, TickitResizeEventInfo);
465         *self = *info;
466 
467         evname = "resize";
468         sv_setref_pv(info_sv, "Tickit::Event::Resize", self);
469         break;
470       }
471     }
472 
473     dSP;
474     ENTER;
475     SAVETMPS;
476 
477     PUSHMARK(SP);
478     EXTEND(SP, 4);
479     mPUSHs(newSVterm(tt, "Tickit::Term"));
480     mPUSHs(newSVivpv(data->ev, evname));
481     mPUSHs(info_sv);
482     mPUSHs(newSVsv(data->data));
483     PUTBACK;
484 
485     call_sv((SV*)(data->code), G_SCALAR);
486 
487     CopLINE_set(PL_curcop, __LINE__);
488     SPAGAIN;
489 
490     ret = POPi;
491 
492     PUTBACK;
493     FREETMPS;
494     LEAVE;
495   }
496 
497   if(flags & TICKIT_EV_UNBIND) {
498     SvREFCNT_dec(data->code);
499     SvREFCNT_dec(data->data);
500     Safefree(data);
501 
502     ret = 1;
503   }
504 
505   return ret;
506 }
507 
term_outputwriter_fn(TickitTerm * tt,const char * bytes,size_t len,void * user)508 static void term_outputwriter_fn(TickitTerm *tt, const char *bytes, size_t len, void *user)
509 {
510   struct GenericEventData *data = user;
511   dTHXa(data->myperl);
512 
513   if(!len) {
514     SvREFCNT_dec(data->data);
515     Safefree(data);
516     return;
517   }
518 
519   dSP;
520   ENTER;
521   SAVETMPS;
522 
523   PUSHMARK(SP);
524   EXTEND(SP, 2);
525   PUSHs(data->data);
526   mPUSHp(bytes, len);
527   PUTBACK;
528 
529   call_method("write", G_VOID);
530 
531   FREETMPS;
532   LEAVE;
533 }
534 
535 /*********************
536  * Tickit::StringPos *
537  *********************/
538 
539 typedef TickitStringPos *Tickit__StringPos;
540 
541 #define new_stringpos(svp)  S_new_stringpos(aTHX_ svp)
S_new_stringpos(pTHX_ SV ** svp)542 static Tickit__StringPos S_new_stringpos(pTHX_ SV **svp)
543 {
544   TickitStringPos *pos;
545 
546   Newx(pos, 1, TickitStringPos);
547   *svp = newSV(0);
548   sv_setref_pv(*svp, "Tickit::StringPos", pos);
549 
550   return pos;
551 }
552 
553 /******************
554  * Tickit::Window *
555  ******************/
556 
557 typedef struct Tickit__Window {
558   TickitWindow *win;
559   SV           *tickit;
560 } *Tickit__Window;
561 
562 /*
563  * We want to wrap every TickitWindow* instance in theabove structure. But we
564  * can't necessarily always do that at construction time, because we don't
565  * necessarily construct all windows. Plus how would we find the structure
566  * wrapping related windows - t_w_root(), _parent(), etc...
567  */
568 static HV *sv_for_window;
569 
window_destroyed(TickitWindow * win,TickitEventFlags flags,void * info,void * user)570 static int window_destroyed(TickitWindow *win, TickitEventFlags flags, void *info, void *user)
571 {
572   struct GenericEventData *data = user;
573   dTHXa(data->myperl);
574 
575   SV *key = newSViv(PTR2UV(win));
576   hv_delete_ent(sv_for_window, key, G_DISCARD, 0);
577   SvREFCNT_dec(key);
578 
579   Safefree(data);
580 
581   return 0;
582 }
583 
584 #define newSVwin_noinc(win)  S_newSVwin_noinc(aTHX_ win)
S_newSVwin_noinc(pTHX_ TickitWindow * win)585 static SV *S_newSVwin_noinc(pTHX_ TickitWindow *win)
586 {
587   if(!sv_for_window)
588     sv_for_window = newHV();
589 
590   SV *key = newSViv(PTR2UV(win));
591   HE *he = hv_fetch_ent(sv_for_window, key, 1, 0);
592   SvREFCNT_dec(key);
593 
594   if(SvOK(HeVAL(he)))
595     return newSVsv(HeVAL(he));
596 
597   struct Tickit__Window *self;
598   Newx(self, 1, struct Tickit__Window);
599   sv_setref_pv(HeVAL(he), "Tickit::Window", self);
600 
601   self->win = win;
602   self->tickit = NULL;
603 
604   tickit_window_bind_event(win, TICKIT_WINDOW_ON_DESTROY, 0, &window_destroyed, new_eventdata(0, NULL, NULL));
605 
606   SV *ret = newSVsv(HeVAL(he));
607   sv_rvweaken(HeVAL(he));
608 
609   return ret;
610 }
611 
612 #define newSVwin(win)  S_newSVwin_noinc(aTHX_ tickit_window_ref(win))
613 
window_userevent_fn(TickitWindow * win,TickitEventFlags flags,void * _info,void * user)614 static int window_userevent_fn(TickitWindow *win, TickitEventFlags flags, void *_info, void *user)
615 {
616   struct GenericEventData *data = user;
617   dTHXa(data->myperl);
618 
619   SET_PL_curcop;
620 
621   int ret = 0;
622 
623   if(flags & TICKIT_EV_FIRE) {
624     SV *info_sv = newSV(0);
625     char *evname = NULL;
626 
627     switch((TickitWindowEvent)data->ev) {
628       case TICKIT_WINDOW_ON_DESTROY:
629         croak("TICKIT_WINDOW_ON_DESTROY should not be TICKIT_EV_FIRE'd");
630         break;
631 
632       case TICKIT_WINDOW_ON_EXPOSE: {
633         TickitExposeEventInfo *info = _info, *self;
634         Newx(self, 1, TickitExposeEventInfo);
635         *self = *info;
636         self->rb = tickit_renderbuffer_ref(info->rb);
637 
638         evname = "expose";
639         sv_setref_pv(info_sv, "Tickit::Event::Expose", self);
640         break;
641       }
642 
643       case TICKIT_WINDOW_ON_GEOMCHANGE: {
644         /* TODO: shouldn't we unpack some arguments? */
645         evname = "geomchange";
646         break;
647       }
648 
649       case TICKIT_WINDOW_ON_FOCUS: {
650         TickitFocusEventInfo *info = _info, *self;
651         Newx(self, 1, TickitFocusEventInfo);
652         *self = *info;
653         self->win = tickit_window_ref(info->win);
654 
655         evname = "focus";
656         sv_setref_pv(info_sv, "Tickit::Event::Focus", self);
657         break;
658       }
659 
660       case TICKIT_WINDOW_ON_KEY: {
661         TickitKeyEventInfo *info = _info, *self;
662         Newx(self, 1, TickitKeyEventInfo);
663         *self = *info;
664         self->str = savepv(info->str);
665 
666         evname = "key";
667         sv_setref_pv(info_sv, "Tickit::Event::Key", self);
668         break;
669       }
670 
671       case TICKIT_WINDOW_ON_MOUSE: {
672         TickitMouseEventInfo *info = _info, *self;
673         Newx(self, 1, TickitMouseEventInfo);
674         *self = *info;
675 
676         evname = "mouse";
677         sv_setref_pv(info_sv, "Tickit::Event::Mouse", self);
678         break;
679       }
680     }
681 
682     dSP;
683     ENTER;
684     SAVETMPS;
685 
686     PUSHMARK(SP);
687     EXTEND(SP, 4);
688     mPUSHs(newSVsv(data->self));
689     mPUSHs(newSVivpv(data->ev, evname));
690     mPUSHs(info_sv);
691     mPUSHs(newSVsv(data->data));
692     PUTBACK;
693 
694     call_sv((SV*)(data->code), G_SCALAR);
695 
696     CopLINE_set(PL_curcop, __LINE__);
697     SPAGAIN;
698 
699     SV *retsv = POPs;
700     ret = SvOK(retsv) ? SvIV(retsv) : 0;
701 
702     PUTBACK;
703     FREETMPS;
704     LEAVE;
705   }
706 
707   if(flags & TICKIT_EV_UNBIND) {
708     SvREFCNT_dec(data->self);
709     SvREFCNT_dec(data->code);
710     SvREFCNT_dec(data->data);
711     Safefree(data);
712 
713     ret = 1;
714   }
715 
716   return ret;
717 }
718 
719 /*******************
720  * toplevel Tickit *
721  *******************/
722 
723 typedef Tickit *Tickit___Tickit;
724 
725 typedef struct {
726 #ifdef tTHX
727   tTHX myperl;
728 #endif
729   CV *cb_init;
730   CV *cb_destroy;
731   CV *cb_run;
732   CV *cb_stop;
733   CV *cb_io;
734   CV *cb_cancel_io;
735   CV *cb_timer;
736   CV *cb_cancel_timer;
737   CV *cb_later;
738   CV *cb_cancel_later;
739   CV *cb_signal;
740   CV *cb_cancel_signal;
741   CV *cb_process;
742   CV *cb_cancel_process;
743 } EventLoopData;
744 
745 #define newSVio_rdonly(fd)  S_newSVio_rdonly(aTHX_ fd)
S_newSVio_rdonly(pTHX_ int fd)746 static SV *S_newSVio_rdonly(pTHX_ int fd)
747 {
748   /* inspired by
749    *   https://metacpan.org/source/LEONT/Linux-Epoll-0.016/lib/Linux/Epoll.xs#L192
750    */
751   PerlIO *pio = PerlIO_fdopen(fd, "r");
752   GV *gv = newGVgen("Tickit::Async");
753   SV *ret = newRV_noinc((SV *)gv);
754   IO *io = GvIOn(gv);
755   IoTYPE(io) = '<';
756   IoIFP(io) = pio;
757   sv_bless(ret, gv_stashpv("IO::Handle", TRUE));
758   return ret;
759 }
760 
761 static XS(invoke_watch);
XS(invoke_watch)762 static XS(invoke_watch)
763 {
764   dXSARGS;
765   TickitWatch *watch = XSANY.any_ptr;
766 
767   tickit_evloop_invoke_watch(watch, TICKIT_EV_FIRE);
768 
769   XSRETURN(0);
770 }
771 
772 #define newSVcallback_tickit_invoke(watch)  S_newSVcallback_tickit_invoke(aTHX_ watch)
S_newSVcallback_tickit_invoke(pTHX_ TickitWatch * watch)773 static SV *S_newSVcallback_tickit_invoke(pTHX_ TickitWatch *watch)
774 {
775   CV *cv = newXS(NULL, invoke_watch, __FILE__);
776   CvXSUBANY(cv).any_ptr = watch;
777   return newRV_noinc((SV *)cv);
778 }
779 
780 static XS(invoke_iowatch);
XS(invoke_iowatch)781 static XS(invoke_iowatch)
782 {
783   dXSARGS;
784   TickitWatch *watch = XSANY.any_ptr;
785 
786   TickitIOCondition cond = POPi;
787 
788   tickit_evloop_invoke_iowatch(watch, TICKIT_EV_FIRE, cond);
789 
790   XSRETURN(0);
791 }
792 
793 #define newSVcallback_tickit_invokeio(watch)  S_newSVcallback_tickit_invokeio(aTHX_ watch)
S_newSVcallback_tickit_invokeio(pTHX_ TickitWatch * watch)794 static SV *S_newSVcallback_tickit_invokeio(pTHX_ TickitWatch *watch)
795 {
796   CV *cv = newXS(NULL, invoke_iowatch, __FILE__);
797   CvXSUBANY(cv).any_ptr = watch;
798   return newRV_noinc((SV *)cv);
799 }
800 
801 static XS(invoke_processwatch);
XS(invoke_processwatch)802 static XS(invoke_processwatch)
803 {
804   dXSARGS;
805   TickitWatch *watch = XSANY.any_ptr;
806 
807   int wstatus = POPi;
808 
809   tickit_evloop_invoke_processwatch(watch, TICKIT_EV_FIRE, wstatus);
810 
811   XSRETURN(0);
812 }
813 
814 #define newSVcallback_tickit_invokeprocess(watch)  S_newSVcallback_tickit_invokeprocess(aTHX_ watch)
S_newSVcallback_tickit_invokeprocess(pTHX_ TickitWatch * watch)815 static SV *S_newSVcallback_tickit_invokeprocess(pTHX_ TickitWatch *watch)
816 {
817   CV *cv = newXS(NULL, invoke_processwatch, __FILE__);
818   CvXSUBANY(cv).any_ptr = watch;
819   return newRV_noinc((SV *)cv);
820 }
821 
822 static XS(invoke_sigwinch);
XS(invoke_sigwinch)823 static XS(invoke_sigwinch)
824 {
825   Tickit *t = XSANY.any_ptr;
826   tickit_evloop_sigwinch(t);
827 }
828 
evloop_init(Tickit * t,void * initdata)829 static void *evloop_init(Tickit *t, void *initdata)
830 {
831   EventLoopData *evdata = initdata;
832   dTHXa(evdata->myperl);
833   SET_PL_curcop;
834 
835   CV *invoke_sigwinch_cv = newXS(NULL, invoke_sigwinch, __FILE__);
836   CvXSUBANY(invoke_sigwinch_cv).any_ptr = t;
837 
838   dSP;
839   SAVETMPS;
840 
841   EXTEND(SP, 1);
842   PUSHMARK(SP);
843   mPUSHs(newRV_noinc((SV *)invoke_sigwinch_cv));
844 
845   PUTBACK;
846 
847   call_sv((SV *)evdata->cb_init, G_VOID);
848 
849   FREETMPS;
850 
851   return initdata;
852 }
853 
evloop_destroy(void * data)854 static void evloop_destroy(void *data)
855 {
856   EventLoopData *evdata = data;
857   dTHXa(evdata->myperl);
858 
859   SvREFCNT_dec(evdata->cb_init);
860   SvREFCNT_dec(evdata->cb_destroy);
861   SvREFCNT_dec(evdata->cb_run);
862   SvREFCNT_dec(evdata->cb_stop);
863   SvREFCNT_dec(evdata->cb_io);
864   SvREFCNT_dec(evdata->cb_cancel_io);
865   SvREFCNT_dec(evdata->cb_timer);
866   SvREFCNT_dec(evdata->cb_cancel_timer);
867   SvREFCNT_dec(evdata->cb_later);
868   SvREFCNT_dec(evdata->cb_cancel_later);
869   SvREFCNT_dec(evdata->cb_signal);
870   SvREFCNT_dec(evdata->cb_cancel_signal);
871   SvREFCNT_dec(evdata->cb_process);
872   SvREFCNT_dec(evdata->cb_cancel_process);
873 }
874 
evloop_run(void * data,TickitRunFlags flags)875 static void evloop_run(void *data, TickitRunFlags flags)
876 {
877   EventLoopData *evdata = data;
878   dTHXa(evdata->myperl);
879   SET_PL_curcop;
880 
881   dSP;
882   SAVETMPS;
883 
884   PUSHMARK(SP);
885   PUTBACK;
886 
887   call_sv((SV *)evdata->cb_run, G_VOID);
888 
889   FREETMPS;
890 }
891 
evloop_stop(void * data)892 static void evloop_stop(void *data)
893 {
894   EventLoopData *evdata = data;
895   dTHXa(evdata->myperl);
896   SET_PL_curcop;
897 
898   dSP;
899   SAVETMPS;
900 
901   PUSHMARK(SP);
902   PUTBACK;
903 
904   call_sv((SV *)evdata->cb_stop, G_VOID);
905 
906   FREETMPS;
907 }
908 
evloop_io(void * data,int fd,TickitIOCondition cond,TickitBindFlags flags,TickitWatch * watch)909 static bool evloop_io(void *data, int fd, TickitIOCondition cond, TickitBindFlags flags, TickitWatch *watch)
910 {
911   EventLoopData *evdata = data;
912   dTHXa(evdata->myperl);
913   SET_PL_curcop;
914 
915   SV *fh = newSVio_rdonly(fd);
916 
917   dSP;
918   SAVETMPS;
919 
920   EXTEND(SP, 3);
921   PUSHMARK(SP);
922 
923   PUSHs(fh);
924   mPUSHi(cond);
925   mPUSHs(newSVcallback_tickit_invokeio(watch));
926   PUTBACK;
927 
928   call_sv((SV *)evdata->cb_io, G_VOID);
929 
930   FREETMPS;
931 
932   tickit_evloop_set_watch_data(watch, fh);
933 
934   return true;
935 }
936 
evloop_cancel_io(void * data,TickitWatch * watch)937 static void evloop_cancel_io(void *data, TickitWatch *watch)
938 {
939   EventLoopData *evdata = data;
940   dTHXa(evdata->myperl);
941   SET_PL_curcop;
942 
943   SV *fh = tickit_evloop_get_watch_data(watch);
944 
945   /* Don't bother during global destruction, as the perl object we're about to
946    * call methods on might not be in a good state any more
947    */
948   if(PL_phase == PERL_PHASE_DESTRUCT)
949     return;
950 
951   dSP;
952   SAVETMPS;
953 
954   EXTEND(SP, 1);
955   PUSHMARK(SP);
956 
957   PUSHs(fh);
958   PUTBACK;
959 
960   call_sv((SV *)evdata->cb_cancel_io, G_VOID);
961 
962   FREETMPS;
963 
964   SvREFCNT_dec(fh);
965   tickit_evloop_set_watch_data(watch, NULL);
966 }
967 
evloop_timer(void * data,const struct timeval * at,TickitBindFlags flags,TickitWatch * watch)968 static bool evloop_timer(void *data, const struct timeval *at, TickitBindFlags flags, TickitWatch *watch)
969 {
970   EventLoopData *evdata = data;
971   dTHXa(evdata->myperl);
972   SET_PL_curcop;
973 
974   NV at_time = at->tv_sec + ((NV)at->tv_usec / 1E6);
975 
976   dSP;
977   SAVETMPS;
978 
979   EXTEND(SP, 2);
980   PUSHMARK(SP);
981 
982   mPUSHn(at_time);
983   mPUSHs(newSVcallback_tickit_invoke(watch));
984   PUTBACK;
985 
986   call_sv((SV *)evdata->cb_timer, G_SCALAR);
987 
988   SPAGAIN;
989 
990   SV *id = SvREFCNT_inc(POPs);
991 
992   FREETMPS;
993 
994   tickit_evloop_set_watch_data(watch, id);
995 
996   return true;
997 }
998 
evloop_cancel_timer(void * data,TickitWatch * watch)999 static void evloop_cancel_timer(void *data, TickitWatch *watch)
1000 {
1001   EventLoopData *evdata = data;
1002   dTHXa(evdata->myperl);
1003   SET_PL_curcop;
1004 
1005   SV *id = tickit_evloop_get_watch_data(watch);
1006 
1007   /* Don't bother during global destruction, as the perl object we're about to
1008    * call methods on might not be in a good state any more
1009    */
1010   if(PL_phase == PERL_PHASE_DESTRUCT)
1011     return;
1012 
1013   dSP;
1014   SAVETMPS;
1015 
1016   EXTEND(SP, 1);
1017   PUSHMARK(SP);
1018 
1019   PUSHs(id);
1020   PUTBACK;
1021 
1022   call_sv((SV *)evdata->cb_cancel_timer, G_VOID);
1023 
1024   FREETMPS;
1025 
1026   SvREFCNT_dec(id);
1027   tickit_evloop_set_watch_data(watch, NULL);
1028 }
1029 
evloop_later(void * data,TickitBindFlags flags,TickitWatch * watch)1030 static bool evloop_later(void *data, TickitBindFlags flags, TickitWatch *watch)
1031 {
1032   EventLoopData *evdata = data;
1033   dTHXa(evdata->myperl);
1034   SET_PL_curcop;
1035 
1036   dSP;
1037   SAVETMPS;
1038 
1039   EXTEND(SP, 1);
1040   PUSHMARK(SP);
1041 
1042   mPUSHs(newSVcallback_tickit_invoke(watch));
1043   PUTBACK;
1044 
1045   call_sv((SV *)evdata->cb_later, G_VOID);
1046 
1047   FREETMPS;
1048 
1049   return true;
1050 }
1051 
evloop_cancel_later(void * data,TickitWatch * watch)1052 static void evloop_cancel_later(void *data, TickitWatch *watch)
1053 {
1054   EventLoopData *evdata = data;
1055   dTHXa(evdata->myperl);
1056   SET_PL_curcop;
1057 
1058   /* Don't bother during global destruction, as the perl object we're about to
1059    * call methods on might not be in a good state any more
1060    */
1061   if(PL_phase == PERL_PHASE_DESTRUCT)
1062     return;
1063 
1064   fprintf(stderr, "Should cancel later here\n");
1065 }
1066 
evloop_signal(void * data,int signum,TickitBindFlags flags,TickitWatch * watch)1067 static bool evloop_signal(void *data, int signum, TickitBindFlags flags, TickitWatch *watch)
1068 {
1069   EventLoopData *evdata = data;
1070   dTHXa(evdata->myperl);
1071   SET_PL_curcop;
1072 
1073   if(!evdata->cb_signal)
1074     return false;
1075 
1076   dSP;
1077   SAVETMPS;
1078 
1079   EXTEND(SP, 2);
1080   PUSHMARK(SP);
1081 
1082   mPUSHi(signum);
1083   mPUSHs(newSVcallback_tickit_invoke(watch));
1084   PUTBACK;
1085 
1086   call_sv((SV *)evdata->cb_signal, G_SCALAR);
1087 
1088   SPAGAIN;
1089 
1090   SV *id = SvREFCNT_inc(POPs);
1091 
1092   FREETMPS;
1093 
1094   tickit_evloop_set_watch_data(watch, id);
1095 
1096   return true;
1097 }
1098 
evloop_cancel_signal(void * data,TickitWatch * watch)1099 static void evloop_cancel_signal(void *data, TickitWatch *watch)
1100 {
1101   EventLoopData *evdata = data;
1102   dTHXa(evdata->myperl);
1103   SET_PL_curcop;
1104 
1105   if(!evdata->cb_cancel_signal)
1106     return;
1107 
1108   SV *id = tickit_evloop_get_watch_data(watch);
1109 
1110   /* Don't bother during global destruction, as the perl object we're about to
1111    * call methods on might not be in a good state any more
1112    */
1113   if(PL_phase == PERL_PHASE_DESTRUCT)
1114     return;
1115 
1116   dSP;
1117   SAVETMPS;
1118 
1119   EXTEND(SP, 1);
1120   PUSHMARK(SP);
1121 
1122   PUSHs(id);
1123   PUTBACK;
1124 
1125   call_sv((SV *)evdata->cb_cancel_signal, G_VOID);
1126 
1127   FREETMPS;
1128 
1129   SvREFCNT_dec(id);
1130   tickit_evloop_set_watch_data(watch, NULL);
1131 }
1132 
evloop_process(void * data,pid_t pid,TickitBindFlags flags,TickitWatch * watch)1133 static bool evloop_process(void *data, pid_t pid, TickitBindFlags flags, TickitWatch *watch)
1134 {
1135   EventLoopData *evdata = data;
1136   dTHXa(evdata->myperl);
1137   SET_PL_curcop;
1138 
1139   if(!evdata->cb_process)
1140     return false;
1141 
1142   dSP;
1143   SAVETMPS;
1144 
1145   EXTEND(SP, 2);
1146   PUSHMARK(SP);
1147 
1148   mPUSHi(pid);
1149   mPUSHs(newSVcallback_tickit_invokeprocess(watch));
1150   PUTBACK;
1151 
1152   call_sv((SV *)evdata->cb_process, G_SCALAR);
1153 
1154   SPAGAIN;
1155 
1156   SV *processid = SvREFCNT_inc(POPs);
1157 
1158   FREETMPS;
1159 
1160   tickit_evloop_set_watch_data(watch, processid);
1161 
1162   return true;
1163 }
1164 
evloop_cancel_process(void * data,TickitWatch * watch)1165 static void evloop_cancel_process(void *data, TickitWatch *watch)
1166 {
1167   EventLoopData *evdata = data;
1168   dTHXa(evdata->myperl);
1169   SET_PL_curcop;
1170 
1171   if(!evdata->cb_cancel_process)
1172     return;
1173 
1174   SV *id = tickit_evloop_get_watch_data(watch);
1175 
1176   /* Don't bother during global destruction, as the perl object we're about to
1177    * call methods on might not be in a good state any more
1178    */
1179   if(PL_phase == PERL_PHASE_DESTRUCT)
1180     return;
1181 
1182   dSP;
1183   SAVETMPS;
1184 
1185   EXTEND(SP, 1);
1186   PUSHMARK(SP);
1187 
1188   PUSHs(id);
1189   PUTBACK;
1190 
1191   call_sv((SV *)evdata->cb_cancel_process, G_VOID);
1192 
1193   FREETMPS;
1194 
1195   SvREFCNT_dec(id);
1196   tickit_evloop_set_watch_data(watch, NULL);
1197 }
1198 
invoke_callback(Tickit * t,TickitEventFlags flags,void * info,void * user)1199 static int invoke_callback(Tickit *t, TickitEventFlags flags, void *info, void *user)
1200 {
1201   struct GenericEventData *data = user;
1202   dTHXa(data->myperl);
1203   dSP;
1204 
1205   SET_PL_curcop;
1206 
1207   if(flags & TICKIT_EV_FIRE) {
1208     ENTER;
1209     SAVETMPS;
1210 
1211     /* No args */
1212     PUSHMARK(SP);
1213     PUTBACK;
1214 
1215     call_sv((SV*)data->code, G_VOID);
1216 
1217     FREETMPS;
1218     LEAVE;
1219   }
1220 
1221   if(flags & TICKIT_EV_UNBIND) {
1222     SvREFCNT_dec((SV*)data->code);
1223     Safefree(data);
1224   }
1225 }
1226 
invoke_iocallback(Tickit * t,TickitEventFlags flags,void * _info,void * user)1227 static int invoke_iocallback(Tickit *t, TickitEventFlags flags, void *_info, void *user)
1228 {
1229   struct GenericEventData *data = user;
1230   dTHXa(data->myperl);
1231   dSP;
1232 
1233   SET_PL_curcop;
1234 
1235   if(flags & TICKIT_EV_FIRE) {
1236     SV *info_sv = newSV(0);
1237     TickitIOWatchInfo *info;
1238     Newx(info, 1, TickitIOWatchInfo);
1239     *info = *(TickitIOWatchInfo *)(_info);
1240     sv_setref_pv(info_sv, "Tickit::Event::IOWatch", info);
1241 
1242     ENTER;
1243     SAVETMPS;
1244 
1245     EXTEND(SP, 1);
1246     PUSHMARK(SP);
1247     mPUSHs(info_sv);
1248     PUTBACK;
1249 
1250     call_sv((SV*)data->code, G_VOID);
1251 
1252     FREETMPS;
1253     LEAVE;
1254   }
1255 
1256   if(flags & TICKIT_EV_UNBIND) {
1257     SvREFCNT_dec((SV*)data->code);
1258     Safefree(data);
1259   }
1260 }
1261 
invoke_processcallback(Tickit * t,TickitEventFlags flags,void * _info,void * user)1262 static int invoke_processcallback(Tickit *t, TickitEventFlags flags, void *_info, void *user)
1263 {
1264   struct GenericEventData *data = user;
1265   dTHXa(data->myperl);
1266   dSP;
1267 
1268   SET_PL_curcop;
1269 
1270   if(flags & TICKIT_EV_FIRE) {
1271     SV *info_sv = newSV(0);
1272     TickitProcessWatchInfo *info;
1273     Newx(info, 1, TickitProcessWatchInfo);
1274     *info = *(TickitProcessWatchInfo *)(_info);
1275     sv_setref_pv(info_sv, "Tickit::Event::ProcessWatch", info);
1276 
1277     ENTER;
1278     SAVETMPS;
1279 
1280     EXTEND(SP, 1);
1281     PUSHMARK(SP);
1282     mPUSHs(info_sv);
1283     PUTBACK;
1284 
1285     call_sv((SV *)data->code, G_VOID);
1286 
1287     FREETMPS;
1288     LEAVE;
1289   }
1290 
1291   if(flags & TICKIT_EV_UNBIND) {
1292     SvREFCNT_dec((SV *)data->code);
1293     Safefree(data);
1294   }
1295 
1296   return 0;
1297 }
1298 
1299 static TickitEventHooks evhooks = {
1300   .init           = evloop_init,
1301   .destroy        = evloop_destroy,
1302   .run            = evloop_run,
1303   .stop           = evloop_stop,
1304   .io             = evloop_io,
1305   .cancel_io      = evloop_cancel_io,
1306   .timer          = evloop_timer,
1307   .cancel_timer   = evloop_cancel_timer,
1308   .later          = evloop_later,
1309   .cancel_later   = evloop_cancel_later,
1310   .signal         = evloop_signal,
1311   .cancel_signal  = evloop_cancel_signal,
1312   .process        = evloop_process,
1313   .cancel_process = evloop_cancel_process,
1314 };
1315 
S_setup_constants(pTHX)1316 static void S_setup_constants(pTHX)
1317 {
1318   HV *stash;
1319   AV *export;
1320 
1321 #define DO_CONSTANT(c) \
1322   newCONSTSUB(stash, #c+7, newSViv(c)); \
1323   av_push(export, newSVpv(#c+7, 0));
1324 
1325   stash = gv_stashpvn("Tickit", 6, TRUE);
1326   export = get_av("Tickit::EXPORT_OK", TRUE);
1327 
1328   DO_CONSTANT(TICKIT_MOD_SHIFT)
1329   DO_CONSTANT(TICKIT_MOD_ALT)
1330   DO_CONSTANT(TICKIT_MOD_CTRL)
1331 
1332   DO_CONSTANT(TICKIT_RUN_NOHANG)
1333   DO_CONSTANT(TICKIT_RUN_NOSETUP)
1334 
1335   DO_CONSTANT(TICKIT_BIND_FIRST)
1336   DO_CONSTANT(TICKIT_BIND_ONESHOT)
1337 
1338   DO_CONSTANT(TICKIT_IO_IN)
1339   DO_CONSTANT(TICKIT_IO_OUT)
1340   DO_CONSTANT(TICKIT_IO_HUP)
1341   DO_CONSTANT(TICKIT_IO_ERR)
1342   DO_CONSTANT(TICKIT_IO_INVAL)
1343 
1344   DO_CONSTANT(TICKIT_FOCUSEV_IN);
1345   DO_CONSTANT(TICKIT_FOCUSEV_OUT);
1346 
1347   DO_CONSTANT(TICKIT_KEYEV_KEY);
1348   DO_CONSTANT(TICKIT_KEYEV_TEXT);
1349 
1350   DO_CONSTANT(TICKIT_MOUSEEV_PRESS);
1351   DO_CONSTANT(TICKIT_MOUSEEV_DRAG);
1352   DO_CONSTANT(TICKIT_MOUSEEV_RELEASE);
1353   DO_CONSTANT(TICKIT_MOUSEEV_WHEEL);
1354   DO_CONSTANT(TICKIT_MOUSEEV_DRAG_START);
1355   DO_CONSTANT(TICKIT_MOUSEEV_DRAG_DROP);
1356   DO_CONSTANT(TICKIT_MOUSEEV_DRAG_STOP);
1357   DO_CONSTANT(TICKIT_MOUSEEV_DRAG_OUTSIDE);
1358 
1359   DO_CONSTANT(TICKIT_MOUSEWHEEL_UP);
1360   DO_CONSTANT(TICKIT_MOUSEWHEEL_DOWN);
1361 
1362   stash = gv_stashpvn("Tickit::Term", 12, TRUE);
1363   export = get_av("Tickit::Term::EXPORT_OK", TRUE);
1364 
1365   DO_CONSTANT(TICKIT_TERMCTL_ALTSCREEN)
1366   DO_CONSTANT(TICKIT_TERMCTL_COLORS)
1367   DO_CONSTANT(TICKIT_TERMCTL_CURSORBLINK)
1368   DO_CONSTANT(TICKIT_TERMCTL_CURSORSHAPE)
1369   DO_CONSTANT(TICKIT_TERMCTL_CURSORVIS)
1370   DO_CONSTANT(TICKIT_TERMCTL_ICON_TEXT)
1371   DO_CONSTANT(TICKIT_TERMCTL_ICONTITLE_TEXT)
1372   DO_CONSTANT(TICKIT_TERMCTL_KEYPAD_APP)
1373   DO_CONSTANT(TICKIT_TERMCTL_MOUSE)
1374   DO_CONSTANT(TICKIT_TERMCTL_TITLE_TEXT)
1375 
1376   DO_CONSTANT(TICKIT_CURSORSHAPE_BLOCK)
1377   DO_CONSTANT(TICKIT_CURSORSHAPE_UNDER)
1378   DO_CONSTANT(TICKIT_CURSORSHAPE_LEFT_BAR)
1379 
1380   DO_CONSTANT(TICKIT_TERM_MOUSEMODE_OFF)
1381   DO_CONSTANT(TICKIT_TERM_MOUSEMODE_CLICK)
1382   DO_CONSTANT(TICKIT_TERM_MOUSEMODE_DRAG)
1383   DO_CONSTANT(TICKIT_TERM_MOUSEMODE_MOVE)
1384 
1385   stash = gv_stashpvn("Tickit::Window", 14, TRUE);
1386   export = get_av("Tickit::Window::EXPORT_OK", TRUE);
1387 
1388   DO_CONSTANT(TICKIT_WINDOW_HIDDEN);
1389   DO_CONSTANT(TICKIT_WINDOW_LOWEST);
1390   DO_CONSTANT(TICKIT_WINDOW_ROOT_PARENT);
1391   DO_CONSTANT(TICKIT_WINDOW_STEAL_INPUT);
1392   DO_CONSTANT(TICKIT_WINDOW_POPUP);
1393 
1394   DO_CONSTANT(TICKIT_WINCTL_CURSORBLINK);
1395   DO_CONSTANT(TICKIT_WINCTL_CURSORSHAPE);
1396   DO_CONSTANT(TICKIT_WINCTL_CURSORVIS);
1397   DO_CONSTANT(TICKIT_WINCTL_FOCUS_CHILD_NOTIFY);
1398   DO_CONSTANT(TICKIT_WINCTL_STEAL_INPUT);
1399 }
1400 
1401 MODULE = Tickit             PACKAGE = Tickit::Debug
1402 
1403 bool
1404 _enabled()
1405   CODE:
1406     RETVAL = tickit_debug_enabled;
1407   OUTPUT:
1408     RETVAL
1409 
1410 void
1411 _log(flag, message)
1412   char *flag
1413   char *message
1414   CODE:
1415     tickit_debug_logf(flag, "%s", message);
1416 
1417 MODULE = Tickit             PACKAGE = Tickit::Event::Expose
1418 
1419 SV *
1420 _new(package,rb,rect)
1421   char                 *package
1422   Tickit::RenderBuffer  rb
1423   Tickit::Rect          rect
1424   INIT:
1425     TickitExposeEventInfo *info;
1426   CODE:
1427     Newx(info, 1, TickitExposeEventInfo);
1428     info->rb   = tickit_renderbuffer_ref(rb);
1429     info->rect = *rect;
1430 
1431     RETVAL = newSV(0);
1432     sv_setref_pv(RETVAL, package, info);
1433   OUTPUT:
1434     RETVAL
1435 
1436 void
1437 DESTROY(self)
1438   SV *self
1439   INIT:
1440     TickitExposeEventInfo *info = INT2PTR(TickitExposeEventInfo *, SvIV((SV*)SvRV(self)));
1441   CODE:
1442     tickit_renderbuffer_unref(info->rb);
1443     Safefree(info);
1444 
1445 SV *
1446 rb(self)
1447   SV *self
1448   ALIAS:
1449     rb   = 0
1450     rect = 1
1451   INIT:
1452     TickitExposeEventInfo *info = INT2PTR(TickitExposeEventInfo *, SvIV((SV*)SvRV(self)));
1453   CODE:
1454     switch(ix) {
1455       case 0: RETVAL = newSVrb(info->rb); break;
1456       case 1: RETVAL = newSVrect(&info->rect); break;
1457       default: croak("Unreachable");
1458     }
1459   OUTPUT:
1460     RETVAL
1461 
1462 MODULE = Tickit             PACKAGE = Tickit::Event::Focus
1463 
1464 SV *
_new(package,type,win)1465 _new(package,type,win)
1466   char *package
1467   SV   *type
1468   SV   *win
1469   INIT:
1470     TickitFocusEventInfo *info;
1471   CODE:
1472     Newx(info, 1, TickitFocusEventInfo);
1473     if(SvPOK(type)) {
1474       info->type = tickit_name2focusev(SvPV_nolen(type));
1475       if(info->type == -1)
1476         croak("Unrecognised focus event type '%s'", SvPV_nolen(type));
1477     }
1478     else
1479       info->type = SvTRUE(type) ? TICKIT_FOCUSEV_IN : TICKIT_FOCUSEV_OUT;
1480 
1481     if(win && SvOK(win))
1482       info->win  = tickit_window_ref(
1483         (INT2PTR(Tickit__Window, SvIV((SV*)SvRV(win))))->win
1484       );
1485     else
1486       info->win = NULL;
1487 
1488     RETVAL = newSV(0);
1489     sv_setref_pv(RETVAL, package, info);
1490   OUTPUT:
1491     RETVAL
1492 
1493 void
1494 DESTROY(self)
1495   SV *self
1496   INIT:
1497     TickitFocusEventInfo *info = INT2PTR(TickitFocusEventInfo *, SvIV((SV*)SvRV(self)));
1498   CODE:
1499     if(info->win)
1500       tickit_window_unref(info->win);
1501     Safefree(info);
1502 
1503 SV *
1504 type(self,newapi=&PL_sv_undef)
1505   SV *self
1506   SV *newapi
1507   ALIAS:
1508     type = 0
1509     win  = 1
1510   INIT:
1511     TickitFocusEventInfo *info = INT2PTR(TickitFocusEventInfo *, SvIV((SV*)SvRV(self)));
1512   CODE:
1513     switch(ix) {
1514       case 0: RETVAL = tickit_focusevtype2sv(info->type); break;
1515       case 1: RETVAL = newSVwin(tickit_window_ref(info->win)); break;
1516       default: croak("Unreachable");
1517     }
1518   OUTPUT:
1519     RETVAL
1520 
1521 MODULE = Tickit             PACKAGE = Tickit::Event::IOWatch
1522 
1523 void
1524 DESTROY(self)
1525   SV *self
1526   INIT:
1527     TickitIOWatchInfo *info = INT2PTR(TickitIOWatchInfo *, SvIV((SV*)SvRV(self)));
1528   CODE:
1529     Safefree(info);
1530 
1531 SV *
1532 fd(self)
1533   SV *self
1534   ALIAS:
1535     fd   = 0
1536     cond = 1
1537   INIT:
1538     TickitIOWatchInfo *info = INT2PTR(TickitIOWatchInfo *, SvIV((SV*)SvRV(self)));
1539   CODE:
1540     switch(ix) {
1541       case 0: RETVAL = newSVuv(info->fd); break;
1542       case 1: RETVAL = newSVuv(info->cond); break;
1543       default: croak("Unreachable");
1544     }
1545   OUTPUT:
1546     RETVAL
1547 
1548 MODULE = Tickit             PACKAGE = Tickit::Event::ProcessWatch
1549 
1550 void
1551 DESTROY(self)
1552   SV *self
1553   INIT:
1554     TickitProcessWatchInfo *info = INT2PTR(TickitProcessWatchInfo *, SvIV((SV*)SvRV(self)));
1555   CODE:
1556     Safefree(info);
1557 
1558 SV *
1559 pid(self)
1560   SV *self
1561   ALIAS:
1562     pid     = 0
1563     wstatus = 1
1564   INIT:
1565     TickitProcessWatchInfo *info = INT2PTR(TickitProcessWatchInfo *, SvIV((SV*)SvRV(self)));
1566   CODE:
1567     switch(ix) {
1568       case 0: RETVAL = newSVuv(info->pid); break;
1569       case 1: RETVAL = newSVuv(info->wstatus); break;
1570       default: croak("Unreachable");
1571     }
1572   OUTPUT:
1573     RETVAL
1574 
1575 
1576 MODULE = Tickit             PACKAGE = Tickit::Event::Key
1577 
1578 SV *
1579 _new(package,type,str,mod=0)
1580   char *package
1581   char *type
1582   char *str
1583   int   mod
1584   INIT:
1585     TickitKeyEventInfo *info;
1586   CODE:
1587     Newx(info, 1, TickitKeyEventInfo);
1588 
1589     info->type = tickit_name2keyev(type);
1590     if(info->type == -1)
1591       croak("Unrecognised key event type '%s'", type);
1592 
1593     info->str  = savepv(str);
1594     info->mod  = mod;
1595 
1596     RETVAL = newSV(0);
1597     sv_setref_pv(RETVAL, package, info);
1598   OUTPUT:
1599     RETVAL
1600 
1601 void
1602 DESTROY(self)
1603   SV *self
1604   INIT:
1605     TickitKeyEventInfo *info = INT2PTR(TickitKeyEventInfo *, SvIV((SV*)SvRV(self)));
1606   CODE:
1607     Safefree(info->str);
1608     Safefree(info);
1609 
1610 SV *
1611 type(self)
1612   SV *self
1613   ALIAS:
1614     type = 0
1615     str  = 1
1616     mod  = 2
1617   INIT:
1618     TickitKeyEventInfo *info = INT2PTR(TickitKeyEventInfo *, SvIV((SV*)SvRV(self)));
1619   CODE:
1620     switch(ix) {
1621       case 0: RETVAL = tickit_keyevtype2sv(info->type); break;
1622       case 1: RETVAL = newSVpvn_utf8(info->str, strlen(info->str), 1); break;
1623       case 2: RETVAL = newSViv(info->mod); break;
1624       default: croak("Unreachable");
1625     }
1626   OUTPUT:
1627     RETVAL
1628 
1629 MODULE = Tickit             PACKAGE = Tickit::Event::Mouse
1630 
1631 SV *
1632 _new(package,type,button,line,col,mod=0)
1633   char *package
1634   char *type
1635   SV   *button
1636   int   line
1637   int   col
1638   int   mod
1639   INIT:
1640     TickitMouseEventInfo *info;
1641   CODE:
1642     Newx(info, 1, TickitMouseEventInfo);
1643 
1644     info->type = tickit_name2mouseev(type);
1645     if(info->type == -1)
1646       croak("Unrecognised mouse event type '%s'", type);
1647 
1648     if(info->type == TICKIT_MOUSEEV_WHEEL) {
1649       info->button = tickit_name2mousewheel(SvPV_nolen(button));
1650       if(info->button == -1)
1651         croak("Unrecognised mouse wheel name '%s'", SvPV_nolen(button));
1652     }
1653     else
1654       info->button = SvIV(button);
1655     info->line = line;
1656     info->col  = col;
1657     info->mod  = mod;
1658 
1659     RETVAL = newSV(0);
1660     sv_setref_pv(RETVAL, package, info);
1661   OUTPUT:
1662     RETVAL
1663 
1664 void
1665 DESTROY(self)
1666   SV *self
1667   CODE:
1668     Safefree(INT2PTR(void *, SvIV((SV*)SvRV(self))));
1669 
1670 SV *
1671 type(self)
1672   SV *self
1673   ALIAS:
1674     type   = 0
1675     button = 1
1676     line   = 2
1677     col    = 3
1678     mod    = 4
1679   INIT:
1680     TickitMouseEventInfo *info = INT2PTR(TickitMouseEventInfo *, SvIV((SV*)SvRV(self)));
1681   CODE:
1682     switch(ix) {
1683       case 0: RETVAL = tickit_mouseevtype2sv(info->type); break;
1684       case 1: RETVAL = tickit_mouseevbutton2sv(info->type, info->button); break;
1685       case 2: RETVAL = newSViv(info->line); break;
1686       case 3: RETVAL = newSViv(info->col); break;
1687       case 4: RETVAL = newSViv(info->mod); break;
1688       default: croak("Unreachable");
1689     }
1690   OUTPUT:
1691     RETVAL
1692 
1693 MODULE = Tickit             PACKAGE = Tickit::Event::Resize
1694 
1695 void
1696 DESTROY(self)
1697   SV *self
1698   CODE:
1699     Safefree(INT2PTR(void *, SvIV((SV*)SvRV(self))));
1700 
1701 SV *
1702 lines(self)
1703   SV *self
1704   ALIAS:
1705     lines = 0
1706     cols  = 1
1707   INIT:
1708     TickitResizeEventInfo *info = INT2PTR(TickitResizeEventInfo *, SvIV((SV*)SvRV(self)));
1709   CODE:
1710     switch(ix) {
1711       case 0: RETVAL = newSViv(info->lines); break;
1712       case 1: RETVAL = newSViv(info->cols);  break;
1713       default: croak("Unreachable");
1714     }
1715   OUTPUT:
1716     RETVAL
1717 
1718 MODULE = Tickit             PACKAGE = Tickit::Pen
1719 
1720 SV *
1721 _new(package, attrs)
1722   char *package
1723   HV   *attrs
1724   INIT:
1725     TickitPen   *pen;
1726   CODE:
1727     pen = tickit_pen_new();
1728     if(!pen)
1729       XSRETURN_UNDEF;
1730 
1731     pen_set_attrs(pen, attrs);
1732 
1733     RETVAL = newSVpen_noinc(pen, package);
1734   OUTPUT:
1735     RETVAL
1736 
1737 void
1738 DESTROY(self)
1739   Tickit::Pen self
1740   CODE:
1741     tickit_pen_unref(self);
1742 
1743 bool
1744 hasattr(self,attr)
1745   Tickit::Pen  self
1746   char        *attr
1747   INIT:
1748     int a;
1749   CODE:
1750     if((a = pen_parse_attrname(attr)) == -1)
1751       XSRETURN_UNDEF;
1752     switch(a) {
1753       case TICKIT_PEN_FG_RGB8:
1754       case TICKIT_PEN_BG_RGB8:
1755         RETVAL = tickit_pen_has_colour_attr_rgb8(self, a & 0xff); break;
1756       default:
1757         RETVAL = tickit_pen_has_attr(self, a); break;
1758     }
1759   OUTPUT:
1760     RETVAL
1761 
1762 SV *
1763 getattr(self,attr)
1764   Tickit::Pen  self
1765   char        *attr
1766   INIT:
1767     int a;
1768   CODE:
1769     if((a = pen_parse_attrname(attr)) == -1)
1770       XSRETURN_UNDEF;
1771     switch(a) {
1772       case TICKIT_PEN_FG_RGB8:
1773       case TICKIT_PEN_BG_RGB8:
1774         if(!tickit_pen_has_colour_attr_rgb8(self, a & 0xff))
1775           XSRETURN_UNDEF;
1776         break;
1777       default:
1778         if(!tickit_pen_has_attr(self, a))
1779           XSRETURN_UNDEF;
1780         break;
1781     }
1782     RETVAL = pen_get_attr(self, a);
1783   OUTPUT:
1784     RETVAL
1785 
1786 void
1787 getattrs(self)
1788   Tickit::Pen self
1789   INIT:
1790     TickitPenAttr a;
1791     int           count = 0;
1792   PPCODE:
1793     for(a = 1; a < TICKIT_N_PEN_ATTRS; a++) {
1794       if(!tickit_pen_has_attr(self, a))
1795         continue;
1796 
1797       EXTEND(SP, 2); count += 2;
1798 
1799       /* Because mPUSHp(str,0) creates a 0-length string */
1800       mPUSHs(newSVpv(tickit_pen_attrname(a), 0));
1801       mPUSHs(pen_get_attr(self, a));
1802     }
1803     if(tickit_pen_has_colour_attr_rgb8(self, TICKIT_PEN_FG)) {
1804       EXTEND(SP, 2); count += 2;
1805       mPUSHs(newSVpv("fg:rgb8", 7));
1806       mPUSHs(pen_get_attr(self, TICKIT_PEN_FG_RGB8));
1807     }
1808     if(tickit_pen_has_colour_attr_rgb8(self, TICKIT_PEN_BG)) {
1809       EXTEND(SP, 2); count += 2;
1810       mPUSHs(newSVpv("bg:rgb8", 7));
1811       mPUSHs(pen_get_attr(self, TICKIT_PEN_BG_RGB8));
1812     }
1813     XSRETURN(count);
1814 
1815 bool
1816 equiv_attr(self,other,attr)
1817   Tickit::Pen  self
1818   Tickit::Pen  other
1819   char        *attr
1820   INIT:
1821     TickitPenAttr a;
1822   CODE:
1823     if((a = tickit_pen_lookup_attr(attr)) == -1)
1824       XSRETURN_UNDEF;
1825     RETVAL = tickit_pen_equiv_attr(self, other, a);
1826   OUTPUT:
1827     RETVAL
1828 
1829 bool
1830 equiv(self,other)
1831   Tickit::Pen  self
1832   Tickit::Pen  other
1833   CODE:
1834     RETVAL = tickit_pen_equiv(self, other);
1835   OUTPUT:
1836     RETVAL
1837 
1838 MODULE = Tickit             PACKAGE = Tickit::Pen::Mutable
1839 
1840 void
1841 chattr(self,attr,value)
1842   Tickit::Pen  self
1843   char        *attr
1844   SV          *value
1845   INIT:
1846     int a;
1847   CODE:
1848     if((a = pen_parse_attrname(attr)) == -1)
1849       XSRETURN_UNDEF;
1850     if(!SvOK(value)) {
1851       switch(a) {
1852         case TICKIT_PEN_FG_RGB8:
1853         case TICKIT_PEN_BG_RGB8:
1854           tickit_pen_set_colour_attr(self, a & 0xFF, tickit_pen_get_colour_attr(self, a & 0xFF));
1855           break;
1856         default:
1857           tickit_pen_clear_attr(self, a);
1858       }
1859       XSRETURN_UNDEF;
1860     }
1861     pen_set_attr(self, a, value);
1862 
1863 void
1864 chattrs(self,attrs)
1865   Tickit::Pen  self
1866   HV          *attrs
1867   CODE:
1868     pen_set_attrs(self, attrs);
1869 
1870 void
1871 delattr(self,attr)
1872   Tickit::Pen  self
1873   char        *attr
1874   INIT:
1875     TickitPenAttr a;
1876   CODE:
1877     if((a = tickit_pen_lookup_attr(attr)) == -1)
1878       XSRETURN_UNDEF;
1879     tickit_pen_clear_attr(self, a);
1880 
1881 void
1882 copy(self,other,overwrite)
1883   Tickit::Pen self
1884   Tickit::Pen other
1885   int         overwrite
1886   CODE:
1887     tickit_pen_copy(self, other, overwrite);
1888 
1889 MODULE = Tickit             PACKAGE = Tickit::Rect
1890 
1891 Tickit::Rect
1892 _new(package,top,left,lines,cols)
1893   char *package
1894   int top
1895   int left
1896   int lines
1897   int cols
1898   CODE:
1899     Newx(RETVAL, 1, TickitRect);
1900     tickit_rect_init_sized(RETVAL, top, left, lines, cols);
1901   OUTPUT:
1902     RETVAL
1903 
1904 void
1905 DESTROY(self)
1906   Tickit::Rect self
1907   CODE:
1908     Safefree(self);
1909 
1910 Tickit::Rect
1911 intersect(self,other)
1912   Tickit::Rect self
1913   Tickit::Rect other
1914   INIT:
1915     TickitRect ret;
1916   CODE:
1917     if(!tickit_rect_intersect(&ret, self, other))
1918       XSRETURN_UNDEF;
1919 
1920     Newx(RETVAL, 1, TickitRect);
1921     *RETVAL = ret;
1922   OUTPUT:
1923     RETVAL
1924 
1925 Tickit::Rect
1926 translate(self,downward,rightward)
1927   Tickit::Rect self
1928   int          downward
1929   int          rightward
1930   CODE:
1931     Newx(RETVAL, 1, TickitRect);
1932     tickit_rect_init_sized(RETVAL, self->top + downward, self->left + rightward,
1933       self->lines, self->cols);
1934   OUTPUT:
1935     RETVAL
1936 
1937 int
1938 top(self)
1939   Tickit::Rect self
1940   CODE:
1941     RETVAL = self->top;
1942   OUTPUT:
1943     RETVAL
1944 
1945 int
1946 left(self)
1947   Tickit::Rect self
1948   CODE:
1949     RETVAL = self->left;
1950   OUTPUT:
1951     RETVAL
1952 
1953 int
1954 lines(self)
1955   Tickit::Rect self
1956   CODE:
1957     RETVAL = self->lines;
1958   OUTPUT:
1959     RETVAL
1960 
1961 int
1962 cols(self)
1963   Tickit::Rect self
1964   CODE:
1965     RETVAL = self->cols;
1966   OUTPUT:
1967     RETVAL
1968 
1969 int
1970 bottom(self)
1971   Tickit::Rect self
1972   CODE:
1973     RETVAL = tickit_rect_bottom(self);
1974   OUTPUT:
1975     RETVAL
1976 
1977 int
1978 right(self)
1979   Tickit::Rect self
1980   CODE:
1981     RETVAL = tickit_rect_right(self);
1982   OUTPUT:
1983     RETVAL
1984 
1985 bool
1986 equals(self,other,swap=0)
1987   Tickit::Rect self
1988   Tickit::Rect other
1989   int          swap
1990   CODE:
1991     RETVAL = (self->top   == other->top) &&
1992              (self->lines == other->lines) &&
1993              (self->left  == other->left) &&
1994              (self->cols  == other->cols);
1995   OUTPUT:
1996     RETVAL
1997 
1998 bool
1999 intersects(self,other)
2000   Tickit::Rect self
2001   Tickit::Rect other
2002   CODE:
2003     RETVAL = tickit_rect_intersects(self, other);
2004   OUTPUT:
2005     RETVAL
2006 
2007 bool
2008 contains(large,small)
2009   Tickit::Rect large
2010   Tickit::Rect small
2011   CODE:
2012     RETVAL = tickit_rect_contains(large, small);
2013   OUTPUT:
2014     RETVAL
2015 
2016 void
2017 add(x,y)
2018   Tickit::Rect x
2019   Tickit::Rect y
2020   INIT:
2021     int n_rects, i;
2022     TickitRect rects[3];
2023   PPCODE:
2024     n_rects = tickit_rect_add(rects, x, y);
2025 
2026     for(i = 0; i < n_rects; i++)
2027       mPUSHrect(rects + i);
2028 
2029     XSRETURN(n_rects);
2030 
2031 void
2032 subtract(self,hole)
2033   Tickit::Rect self
2034   Tickit::Rect hole
2035   INIT:
2036     int n_rects, i;
2037     TickitRect rects[4];
2038   PPCODE:
2039     n_rects = tickit_rect_subtract(rects, self, hole);
2040 
2041     EXTEND(SP, n_rects);
2042     for(i = 0; i < n_rects; i++)
2043       mPUSHrect(rects + i);
2044 
2045     XSRETURN(n_rects);
2046 
2047 MODULE = Tickit             PACKAGE = Tickit::RectSet
2048 
2049 Tickit::RectSet
2050 new(package)
2051   char *package
2052   CODE:
2053     RETVAL = tickit_rectset_new();
2054   OUTPUT:
2055     RETVAL
2056 
2057 void
2058 DESTROY(self)
2059   Tickit::RectSet self
2060   CODE:
2061     tickit_rectset_destroy(self);
2062 
2063 void
2064 clear(self)
2065   Tickit::RectSet self
2066   CODE:
2067     tickit_rectset_clear(self);
2068 
2069 void
2070 rects(self)
2071   Tickit::RectSet self
2072   INIT:
2073     int n;
2074     int i;
2075   PPCODE:
2076     n = tickit_rectset_rects(self);
2077 
2078     if(GIMME_V != G_ARRAY) {
2079       mPUSHi(n);
2080       XSRETURN(1);
2081     }
2082 
2083     EXTEND(SP, n);
2084     for(i = 0; i < n; i++) {
2085       TickitRect rect;
2086       tickit_rectset_get_rect(self, i, &rect);
2087       mPUSHrect(&rect);
2088     }
2089 
2090     XSRETURN(n);
2091 
2092 void
2093 add(self,rect)
2094   Tickit::RectSet self
2095   Tickit::Rect rect
2096   CODE:
2097     tickit_rectset_add(self, rect);
2098 
2099 void
2100 subtract(self,rect)
2101   Tickit::RectSet self
2102   Tickit::Rect rect
2103   CODE:
2104     tickit_rectset_subtract(self, rect);
2105 
2106 bool
2107 intersects(self,r)
2108   Tickit::RectSet self
2109   Tickit::Rect r
2110   CODE:
2111     RETVAL = tickit_rectset_intersects(self, r);
2112   OUTPUT:
2113     RETVAL
2114 
2115 bool
2116 contains(self,r)
2117   Tickit::RectSet self
2118   Tickit::Rect r
2119   CODE:
2120     RETVAL = tickit_rectset_contains(self, r);
2121   OUTPUT:
2122     RETVAL
2123 
2124 MODULE = Tickit             PACKAGE = Tickit::RenderBuffer
2125 
2126 SV *
2127 _xs_new(class,lines,cols)
2128   char *class
2129   int lines
2130   int cols
2131   CODE:
2132     RETVAL = newSVrb_noinc(tickit_renderbuffer_new(lines, cols));
2133   OUTPUT:
2134     RETVAL
2135 
2136 void
2137 DESTROY(self)
2138   Tickit::RenderBuffer self
2139   CODE:
2140     tickit_renderbuffer_unref(self);
2141 
2142 int
2143 lines(self)
2144   Tickit::RenderBuffer self
2145   CODE:
2146     tickit_renderbuffer_get_size(self, &RETVAL, NULL);
2147   OUTPUT:
2148     RETVAL
2149 
2150 int
2151 cols(self)
2152   Tickit::RenderBuffer self
2153   CODE:
2154     tickit_renderbuffer_get_size(self, NULL, &RETVAL);
2155   OUTPUT:
2156     RETVAL
2157 
2158 SV *
2159 line(self)
2160   Tickit::RenderBuffer self
2161   INIT:
2162     TickitRenderBuffer *rb;
2163   CODE:
2164     rb = self;
2165     if(tickit_renderbuffer_has_cursorpos(rb)) {
2166       int line;
2167       tickit_renderbuffer_get_cursorpos(rb, &line, NULL);
2168       RETVAL = newSViv(line);
2169     }
2170     else
2171       RETVAL = &PL_sv_undef;
2172   OUTPUT:
2173     RETVAL
2174 
2175 SV *
2176 col(self)
2177   Tickit::RenderBuffer self
2178   INIT:
2179     TickitRenderBuffer *rb;
2180   CODE:
2181     rb = self;
2182     if(tickit_renderbuffer_has_cursorpos(rb)) {
2183       int col;
2184       tickit_renderbuffer_get_cursorpos(rb, NULL, &col);
2185       RETVAL = newSViv(col);
2186     }
2187     else
2188       RETVAL = &PL_sv_undef;
2189   OUTPUT:
2190     RETVAL
2191 
2192 void
2193 translate(self,downward,rightward)
2194   Tickit::RenderBuffer self
2195   int downward
2196   int rightward
2197   PPCODE:
2198     tickit_renderbuffer_translate(self, downward, rightward);
2199 
2200 void
2201 clip(self,rect)
2202   Tickit::RenderBuffer self
2203   Tickit::Rect rect
2204   CODE:
2205     tickit_renderbuffer_clip(self, rect);
2206 
2207 void
2208 mask(self,rect)
2209   Tickit::RenderBuffer self
2210   Tickit::Rect rect
2211   CODE:
2212     tickit_renderbuffer_mask(self, rect);
2213 
2214 void
2215 goto(self,line,col)
2216   Tickit::RenderBuffer self
2217   SV *line
2218   SV *col
2219   CODE:
2220     if(SvIsNumeric(line) && SvIsNumeric(col))
2221       tickit_renderbuffer_goto(self, SvIV(line), SvIV(col));
2222     else
2223       tickit_renderbuffer_ungoto(self);
2224 
2225 void
2226 setpen(self,pen)
2227   Tickit::RenderBuffer self
2228   Tickit::Pen pen
2229   CODE:
2230     tickit_renderbuffer_setpen(self, pen);
2231 
2232 void
2233 reset(self)
2234   Tickit::RenderBuffer self
2235   CODE:
2236     tickit_renderbuffer_reset(self);
2237 
2238 void
2239 clear(self,pen=NULL)
2240   Tickit::RenderBuffer self
2241   Tickit::Pen pen
2242   CODE:
2243     if(pen) {
2244       tickit_renderbuffer_savepen(self);
2245       tickit_renderbuffer_setpen(self, pen);
2246     }
2247     tickit_renderbuffer_clear(self);
2248     if(pen)
2249       tickit_renderbuffer_restore(self);
2250 
2251 void
2252 save(self)
2253   Tickit::RenderBuffer self
2254   CODE:
2255     tickit_renderbuffer_save(self);
2256 
2257 void
2258 savepen(self)
2259   Tickit::RenderBuffer self
2260   CODE:
2261     tickit_renderbuffer_savepen(self);
2262 
2263 void
2264 restore(self)
2265   Tickit::RenderBuffer self
2266   CODE:
2267     tickit_renderbuffer_restore(self);
2268 
2269 void
2270 _xs_get_cell(self,line,col)
2271   Tickit::RenderBuffer self
2272   int line
2273   int col
2274   INIT:
2275     TickitRenderBuffer *rb;
2276     STRLEN len;
2277     SV *text;
2278     TickitRenderBufferLineMask mask;
2279   PPCODE:
2280     rb = self;
2281     if(tickit_renderbuffer_get_cell_active(rb, line, col) != 1) {
2282       XPUSHs(&PL_sv_undef);
2283       XPUSHs(&PL_sv_undef);
2284       XSRETURN(2);
2285     }
2286 
2287     EXTEND(SP, 6);
2288 
2289     len = tickit_renderbuffer_get_cell_text(rb, line, col, NULL, 0);
2290     text = newSV(len + 1);
2291     tickit_renderbuffer_get_cell_text(rb, line, col, SvPVX(text), len + 1);
2292     SvPOK_on(text); SvUTF8_on(text); SvCUR_set(text, len);
2293     mPUSHs(text);
2294 
2295     mPUSHs(newSVpen_noinc(tickit_pen_clone(tickit_renderbuffer_get_cell_pen(rb, line, col)), NULL));
2296 
2297     mask = tickit_renderbuffer_get_cell_linemask(rb, line, col);
2298     if(!mask.north && !mask.south && !mask.east && !mask.west)
2299       XSRETURN(2);
2300 
2301     mPUSHi(mask.north);
2302     mPUSHi(mask.south);
2303     mPUSHi(mask.east);
2304     mPUSHi(mask.west);
2305     XSRETURN(6);
2306 
2307 void
2308 skip_at(self,line,col,len)
2309   Tickit::RenderBuffer self
2310   int line
2311   int col
2312   int len
2313   CODE:
2314     tickit_renderbuffer_skip_at(self, line, col, len);
2315 
2316 void
2317 skip(self,len)
2318   Tickit::RenderBuffer self
2319   int len
2320   CODE:
2321     if(!tickit_renderbuffer_has_cursorpos(self))
2322       croak("Cannot ->skip without a virtual cursor position");
2323 
2324     tickit_renderbuffer_skip(self, len);
2325 
2326 void
2327 skip_to(self,col)
2328   Tickit::RenderBuffer self
2329   int col
2330   CODE:
2331     if(!tickit_renderbuffer_has_cursorpos(self))
2332       croak("Cannot ->skip_to without a virtual cursor position");
2333 
2334     tickit_renderbuffer_skip_to(self, col);
2335 
2336 void
2337 skiprect(self,rect)
2338   Tickit::RenderBuffer self
2339   Tickit::Rect rect
2340   CODE:
2341     tickit_renderbuffer_skiprect(self, rect);
2342 
2343 int
2344 text_at(self,line,col,text,pen=NULL)
2345   Tickit::RenderBuffer self
2346   int line
2347   int col
2348   SV *text
2349   Tickit::Pen pen
2350   INIT:
2351     char *bytes;
2352     STRLEN len;
2353   CODE:
2354     bytes = SvPVutf8(text, len);
2355     if(pen) {
2356       tickit_renderbuffer_savepen(self);
2357       tickit_renderbuffer_setpen(self, pen);
2358     }
2359     RETVAL = tickit_renderbuffer_textn_at(self, line, col, bytes, len);
2360     if(pen)
2361       tickit_renderbuffer_restore(self);
2362   OUTPUT:
2363     RETVAL
2364 
2365 int
2366 text(self,text,pen=NULL)
2367   Tickit::RenderBuffer self
2368   SV *text
2369   Tickit::Pen pen
2370   INIT:
2371     char *bytes;
2372     STRLEN len;
2373   CODE:
2374     if(!tickit_renderbuffer_has_cursorpos(self))
2375       croak("Cannot ->text without a virtual cursor position");
2376 
2377     bytes = SvPVutf8(text, len);
2378     if(pen) {
2379       tickit_renderbuffer_savepen(self);
2380       tickit_renderbuffer_setpen(self, pen);
2381     }
2382     RETVAL = tickit_renderbuffer_textn(self, bytes, len);
2383     if(pen)
2384       tickit_renderbuffer_restore(self);
2385   OUTPUT:
2386     RETVAL
2387 
2388 void
2389 erase_at(self,line,col,len,pen=NULL)
2390   Tickit::RenderBuffer self
2391   int line
2392   int col
2393   int len
2394   Tickit::Pen pen
2395   CODE:
2396     if(pen) {
2397       tickit_renderbuffer_savepen(self);
2398       tickit_renderbuffer_setpen(self, pen);
2399     }
2400     tickit_renderbuffer_erase_at(self, line, col, len);
2401     if(pen)
2402       tickit_renderbuffer_restore(self);
2403 
2404 void
2405 erase(self,len,pen=NULL)
2406   Tickit::RenderBuffer self
2407   int len
2408   Tickit::Pen pen
2409   CODE:
2410     if(!tickit_renderbuffer_has_cursorpos(self))
2411       croak("Cannot ->erase without a virtual cursor position");
2412 
2413     if(pen) {
2414       tickit_renderbuffer_savepen(self);
2415       tickit_renderbuffer_setpen(self, pen);
2416     }
2417     tickit_renderbuffer_erase(self, len);
2418     if(pen)
2419       tickit_renderbuffer_restore(self);
2420 
2421 void
2422 erase_to(self,col,pen=NULL)
2423   Tickit::RenderBuffer self
2424   int col
2425   Tickit::Pen pen
2426   CODE:
2427     if(!tickit_renderbuffer_has_cursorpos(self))
2428       croak("Cannot ->erase_to without a virtual cursor position");
2429 
2430     if(pen) {
2431       tickit_renderbuffer_savepen(self);
2432       tickit_renderbuffer_setpen(self, pen);
2433     }
2434     tickit_renderbuffer_erase_to(self, col);
2435     if(pen)
2436       tickit_renderbuffer_restore(self);
2437 
2438 void
2439 eraserect(self,rect,pen=NULL)
2440   Tickit::RenderBuffer self
2441   Tickit::Rect rect
2442   Tickit::Pen pen
2443   CODE:
2444     if(pen) {
2445       tickit_renderbuffer_savepen(self);
2446       tickit_renderbuffer_setpen(self, pen);
2447     }
2448     tickit_renderbuffer_eraserect(self, rect);
2449     if(pen)
2450       tickit_renderbuffer_restore(self);
2451 
2452 void
2453 char_at(self,line,col,codepoint,pen=NULL)
2454   Tickit::RenderBuffer self
2455   int line
2456   int col
2457   int codepoint
2458   Tickit::Pen pen
2459   CODE:
2460     if(pen) {
2461       tickit_renderbuffer_savepen(self);
2462       tickit_renderbuffer_setpen(self, pen);
2463     }
2464     tickit_renderbuffer_char_at(self, line, col, codepoint);
2465     if(pen)
2466       tickit_renderbuffer_restore(self);
2467 
2468 void
2469 char(self,codepoint,pen=NULL)
2470   Tickit::RenderBuffer self
2471   int codepoint
2472   Tickit::Pen pen
2473   CODE:
2474     if(pen) {
2475       tickit_renderbuffer_savepen(self);
2476       tickit_renderbuffer_setpen(self, pen);
2477     }
2478     tickit_renderbuffer_char(self, codepoint);
2479     if(pen)
2480       tickit_renderbuffer_restore(self);
2481 
2482 void
2483 hline_at(self,line,startcol,endcol,style,pen=NULL,caps=0)
2484   Tickit::RenderBuffer self
2485   int line
2486   int startcol
2487   int endcol
2488   int style
2489   Tickit::Pen pen
2490   int caps
2491   CODE:
2492     if(pen) {
2493       tickit_renderbuffer_savepen(self);
2494       tickit_renderbuffer_setpen(self, pen);
2495     }
2496     tickit_renderbuffer_hline_at(self, line, startcol, endcol, style, caps);
2497     if(pen)
2498       tickit_renderbuffer_restore(self);
2499 
2500 void
2501 vline_at(self,startline,endline,col,style,pen=NULL,caps=0)
2502   Tickit::RenderBuffer self
2503   int startline
2504   int endline
2505   int col
2506   int style
2507   Tickit::Pen pen
2508   int caps
2509   CODE:
2510     if(pen) {
2511       tickit_renderbuffer_savepen(self);
2512       tickit_renderbuffer_setpen(self, pen);
2513     }
2514     tickit_renderbuffer_vline_at(self, startline, endline, col, style, caps);
2515     if(pen)
2516       tickit_renderbuffer_restore(self);
2517 
2518 void
2519 flush_to_term(self,term)
2520   Tickit::RenderBuffer self
2521   Tickit::Term term
2522   CODE:
2523     tickit_renderbuffer_flush_to_term(self, term);
2524 
2525 void
2526 copyrect(self,dest,src)
2527   Tickit::RenderBuffer self
2528   Tickit::Rect dest
2529   Tickit::Rect src
2530   ALIAS:
2531     copyrect = 0
2532     moverect = 1
2533   CODE:
2534     switch(ix) {
2535       case 0: tickit_renderbuffer_copyrect(self, dest, src); break;
2536       case 1: tickit_renderbuffer_moverect(self, dest, src); break;
2537     }
2538 
2539 MODULE = Tickit             PACKAGE = Tickit::StringPos
2540 
2541 SV *
2542 zero(package)
2543   char *package;
2544   INIT:
2545     TickitStringPos *pos;
2546   CODE:
2547     pos = new_stringpos(&RETVAL);
2548     tickit_stringpos_zero(pos);
2549   OUTPUT:
2550     RETVAL
2551 
2552 SV *
2553 limit_bytes(package,bytes)
2554   char *package;
2555   size_t bytes;
2556   INIT:
2557     TickitStringPos *pos;
2558   CODE:
2559     pos = new_stringpos(&RETVAL);
2560     tickit_stringpos_limit_bytes(pos, bytes);
2561   OUTPUT:
2562     RETVAL
2563 
2564 SV *
2565 limit_codepoints(package,codepoints)
2566   char *package;
2567   int codepoints;
2568   INIT:
2569     TickitStringPos *pos;
2570   CODE:
2571     pos = new_stringpos(&RETVAL);
2572     tickit_stringpos_limit_codepoints(pos, codepoints);
2573   OUTPUT:
2574     RETVAL
2575 
2576 SV *
2577 limit_graphemes(package,graphemes)
2578   char *package;
2579   int graphemes;
2580   INIT:
2581     TickitStringPos *pos;
2582   CODE:
2583     pos = new_stringpos(&RETVAL);
2584     tickit_stringpos_limit_graphemes(pos, graphemes);
2585   OUTPUT:
2586     RETVAL
2587 
2588 SV *
2589 limit_columns(package,columns)
2590   char *package;
2591   int columns;
2592   INIT:
2593     TickitStringPos *pos;
2594   CODE:
2595     pos = new_stringpos(&RETVAL);
2596     tickit_stringpos_limit_columns(pos, columns);
2597   OUTPUT:
2598     RETVAL
2599 
2600 void
2601 DESTROY(self)
2602   Tickit::StringPos self
2603   CODE:
2604     Safefree(self);
2605 
2606 size_t
2607 bytes(self)
2608   Tickit::StringPos self;
2609   CODE:
2610     RETVAL = self->bytes;
2611   OUTPUT:
2612     RETVAL
2613 
2614 int
2615 codepoints(self)
2616   Tickit::StringPos self;
2617   CODE:
2618     RETVAL = self->codepoints;
2619   OUTPUT:
2620     RETVAL
2621 
2622 int
2623 graphemes(self)
2624   Tickit::StringPos self;
2625   CODE:
2626     RETVAL = self->graphemes;
2627   OUTPUT:
2628     RETVAL
2629 
2630 int
2631 columns(self)
2632   Tickit::StringPos self;
2633   CODE:
2634     RETVAL = self->columns;
2635   OUTPUT:
2636     RETVAL
2637 
2638 MODULE = Tickit             PACKAGE = Tickit::Term
2639 
2640 SV *
_new(package,termtype,input_handle,output_handle,writer,utf8)2641 _new(package,termtype,input_handle,output_handle,writer,utf8)
2642   char *package;
2643   char *termtype;
2644   SV   *input_handle
2645   SV   *output_handle
2646   SV   *writer
2647   SV   *utf8
2648   INIT:
2649     struct TickitTermBuilder builder = { 0 };
2650     TickitTerm   *tt;
2651   CODE:
2652     builder.termtype = termtype;
2653     builder.open = TICKIT_OPEN_FDS;
2654     builder.input_fd = -1;
2655     builder.output_fd = -1;
2656 
2657     if(SvOK(input_handle))
2658       builder.input_fd = PerlIO_fileno(IoIFP(sv_2io(input_handle)));
2659     if(SvOK(output_handle))
2660       builder.output_fd = PerlIO_fileno(IoOFP(sv_2io(output_handle)));
2661     if(SvOK(writer)) {
2662       builder.output_func      = term_outputwriter_fn;
2663       builder.output_func_user = new_eventdata(0, SvREFCNT_inc(writer), NULL);
2664     }
2665 
2666     tt = tickit_term_build(&builder);
2667     if(!tt)
2668       XSRETURN_UNDEF;
2669 
2670     if(SvOK(utf8))
2671       tickit_term_set_utf8(tt, SvTRUE(utf8));
2672 
2673     RETVAL = newSVterm_noinc(tt, package);
2674   OUTPUT:
2675     RETVAL
2676 
2677 SV *
2678 open_stdio(package)
2679   char *package
2680   INIT:
2681     TickitTerm   *tt;
2682   CODE:
2683     tt = tickit_term_open_stdio();
2684     if(!tt)
2685       XSRETURN_UNDEF;
2686 
2687     RETVAL = newSVterm_noinc(tt, package);
2688   OUTPUT:
2689     RETVAL
2690 
2691 void
2692 DESTROY(self)
2693   Tickit::Term  self
2694   CODE:
2695     /*
2696      * destroy TickitTerm first in case it's still using output_handle/func
2697      */
2698     tickit_term_unref(self);
2699 
2700 UV
2701 _xs_addr(self, ...)
2702   Tickit::Term  self
2703   CODE:
2704     RETVAL = (UV)self;
2705   OUTPUT:
2706     RETVAL
2707 
2708 int
2709 get_input_fd(self)
2710   Tickit::Term  self
2711   CODE:
2712     RETVAL = tickit_term_get_input_fd(self);
2713   OUTPUT:
2714     RETVAL
2715 
2716 int
2717 get_output_fd(self)
2718   Tickit::Term  self
2719   CODE:
2720     RETVAL = tickit_term_get_output_fd(self);
2721   OUTPUT:
2722     RETVAL
2723 
2724 void
2725 await_started(self,timeout)
2726   Tickit::Term  self
2727   double        timeout
2728   CODE:
2729     tickit_term_await_started_msec(self, timeout * 1000);
2730 
2731 void
2732 pause(self)
2733   Tickit::Term  self
2734   CODE:
2735     tickit_term_pause(self);
2736 
2737 void
2738 resume(self)
2739   Tickit::Term  self
2740   CODE:
2741     tickit_term_resume(self);
2742 
2743 void
2744 teardown(self)
2745   Tickit::Term  self
2746   CODE:
2747     tickit_term_teardown(self);
2748 
2749 void
2750 flush(self)
2751   Tickit::Term  self
2752   CODE:
2753     tickit_term_flush(self);
2754 
2755 void
2756 set_output_buffer(self,len)
2757   Tickit::Term  self
2758   size_t        len
2759   CODE:
2760     tickit_term_set_output_buffer(self, len);
2761 
2762 void
2763 get_size(self)
2764   Tickit::Term  self
2765   INIT:
2766     int lines, cols;
2767   PPCODE:
2768     tickit_term_get_size(self, &lines, &cols);
2769     EXTEND(SP, 2);
2770     mPUSHi(lines);
2771     mPUSHi(cols);
2772     XSRETURN(2);
2773 
2774 void
2775 set_size(self,lines,cols)
2776   Tickit::Term  self
2777   int           lines
2778   int           cols
2779   CODE:
2780     tickit_term_set_size(self, lines, cols);
2781 
2782 void
2783 refresh_size(self)
2784   Tickit::Term  self
2785   CODE:
2786     tickit_term_refresh_size(self);
2787 
2788 int
2789 _bind_event(self,ev,flags,code,data = &PL_sv_undef)
2790   Tickit::Term  self
2791   char         *ev
2792   int           flags
2793   CV           *code
2794   SV           *data
2795   INIT:
2796     TickitTermEvent _ev = -1;
2797     struct GenericEventData *user;
2798   CODE:
2799     switch(ev[0]) {
2800       case 'k':
2801         if(strEQ(ev, "key"))
2802           _ev = TICKIT_TERM_ON_KEY;
2803         break;
2804       case 'm':
2805         if(strEQ(ev, "mouse"))
2806           _ev = TICKIT_TERM_ON_MOUSE;
2807         break;
2808       case 'r':
2809         if(strEQ(ev, "resize"))
2810           _ev = TICKIT_TERM_ON_RESIZE;
2811         break;
2812     }
2813     if(_ev == -1)
2814       croak("Unrecognised event name '%s'", ev);
2815 
2816     user = new_eventdata(_ev, newSVsv(data), code);
2817 
2818     RETVAL = tickit_term_bind_event(self, _ev, flags|TICKIT_EV_UNBIND, term_userevent_fn, user);
2819   OUTPUT:
2820     RETVAL
2821 
2822 void
2823 unbind_event_id(self,id)
2824   Tickit::Term  self
2825   int           id
2826   CODE:
2827     tickit_term_unbind_event_id(self, id);
2828 
2829 void
2830 input_push_bytes(self,bytes)
2831   Tickit::Term  self
2832   SV           *bytes
2833   INIT:
2834     char   *str;
2835     STRLEN  len;
2836   CODE:
2837     str = SvPV(bytes, len);
2838     tickit_term_input_push_bytes(self, str, len);
2839 
2840 void
2841 input_readable(self)
2842   Tickit::Term  self
2843   CODE:
2844     tickit_term_input_readable(self);
2845 
2846 void
2847 input_wait(self,timeout=&PL_sv_undef)
2848   Tickit::Term  self
2849   SV           *timeout
2850   CODE:
2851     if(SvIsNumeric(timeout))
2852       tickit_term_input_wait_msec(self, SvNV(timeout) * 1000);
2853     else
2854       tickit_term_input_wait_msec(self, -1);
2855 
2856 
2857 SV *
2858 check_timeout(self)
2859   Tickit::Term  self
2860   INIT:
2861     int msec;
2862   CODE:
2863     msec = tickit_term_input_check_timeout_msec(self);
2864     RETVAL = newSV(0);
2865     if(msec >= 0)
2866       sv_setnv(RETVAL, msec / 1000.0);
2867   OUTPUT:
2868     RETVAL
2869 
2870 bool
2871 goto(self,line,col)
2872   Tickit::Term  self
2873   SV           *line
2874   SV           *col
2875   CODE:
2876     RETVAL = tickit_term_goto(self, SvOK(line) ? SvIV(line) : -1, SvOK(col) ? SvIV(col) : -1);
2877   OUTPUT:
2878     RETVAL
2879 
2880 void
2881 move(self,downward,rightward)
2882   Tickit::Term  self
2883   SV           *downward
2884   SV           *rightward
2885   CODE:
2886     tickit_term_move(self, SvOK(downward) ? SvIV(downward) : 0, SvOK(rightward) ? SvIV(rightward) : 0);
2887 
2888 int
2889 scrollrect(self,top,left,lines,cols,downward,rightward)
2890   Tickit::Term  self
2891   int           top
2892   int           left
2893   int           lines
2894   int           cols
2895   int           downward
2896   int           rightward
2897   INIT:
2898     TickitRect rect;
2899   CODE:
2900     rect.top   = top;
2901     rect.left  = left;
2902     rect.lines = lines;
2903     rect.cols  = cols;
2904     RETVAL = tickit_term_scrollrect(self, rect, downward, rightward);
2905   OUTPUT:
2906     RETVAL
2907 
2908 void
2909 chpen(self,...)
2910   Tickit::Term  self
2911   INIT:
2912     TickitPen *pen;
2913     int        pen_temp = 0;
2914   CODE:
2915     if(items == 2 && SvROK(ST(1)) && sv_derived_from(ST(1), "Tickit::Pen")) {
2916       IV tmp = SvIV((SV*)SvRV(ST(1)));
2917       Tickit__Pen self = INT2PTR(Tickit__Pen, tmp);
2918       pen = self;
2919     }
2920     else {
2921       pen = pen_from_args(SP-items+2, items-1);
2922       pen_temp = 1;
2923     }
2924     tickit_term_chpen(self, pen);
2925     if(pen_temp)
2926       tickit_pen_unref(pen);
2927 
2928 void
2929 setpen(self,...)
2930   Tickit::Term  self
2931   INIT:
2932     TickitPen *pen;
2933     int        pen_temp = 0;
2934   CODE:
2935     if(items == 2 && SvROK(ST(1)) && sv_derived_from(ST(1), "Tickit::Pen")) {
2936       IV tmp = SvIV((SV*)SvRV(ST(1)));
2937       Tickit__Pen self = INT2PTR(Tickit__Pen, tmp);
2938       pen = self;
2939     }
2940     else {
2941       pen = pen_from_args(SP-items+2, items-1);
2942       pen_temp = 1;
2943     }
2944     tickit_term_setpen(self, pen);
2945     if(pen_temp)
2946       tickit_pen_unref(pen);
2947 
2948 void
2949 print(self,text,pen=NULL)
2950   Tickit::Term  self
2951   SV           *text
2952   Tickit::Pen   pen
2953   INIT:
2954     char  *utf8;
2955     STRLEN len;
2956   CODE:
2957     if(pen)
2958       tickit_term_setpen(self, pen);
2959     utf8 = SvPVutf8(text, len);
2960     tickit_term_printn(self, utf8, len);
2961 
2962 void
2963 clear(self,pen=NULL)
2964   Tickit::Term  self
2965   Tickit::Pen   pen
2966   CODE:
2967     if(pen)
2968       tickit_term_setpen(self, pen);
2969     tickit_term_clear(self);
2970 
2971 void
2972 erasech(self,count,moveend,pen=NULL)
2973   Tickit::Term  self
2974   int           count
2975   SV           *moveend
2976   Tickit::Pen   pen
2977   CODE:
2978     if(pen)
2979       tickit_term_setpen(self, pen);
2980     tickit_term_erasech(self, count, SvOK(moveend) ? SvIV(moveend) : -1);
2981 
2982 int
2983 getctl_int(self,ctl)
2984   Tickit::Term self
2985   SV          *ctl
2986   INIT:
2987     TickitTermCtl ctl_e;
2988   CODE:
2989     if(SvPOK(ctl)) {
2990       ctl_e = tickit_term_lookup_ctl(SvPV_nolen(ctl));
2991       if(ctl_e == -1)
2992         croak("Unrecognised 'ctl' name '%s'", SvPV_nolen(ctl));
2993     }
2994     else if(SvIOK(ctl))
2995       ctl_e = SvIV(ctl);
2996     else
2997       croak("Expected 'ctl' to be an integer or string");
2998 
2999     if(!tickit_term_getctl_int(self, ctl_e, &RETVAL))
3000       XSRETURN_UNDEF;
3001   OUTPUT:
3002     RETVAL
3003 
3004 void
3005 setctl_int(self,ctl,value)
3006   Tickit::Term self
3007   SV          *ctl
3008   int          value
3009   INIT:
3010     TickitTermCtl ctl_e;
3011   PPCODE:
3012     if(SvPOK(ctl)) {
3013       ctl_e = tickit_term_lookup_ctl(SvPV_nolen(ctl));
3014       if(ctl_e == -1)
3015         croak("Unrecognised 'ctl' name '%s'", SvPV_nolen(ctl));
3016     }
3017     else if(SvIOK(ctl))
3018       ctl_e = SvIV(ctl);
3019     else
3020       croak("Expected 'ctl' to be an integer or string");
3021 
3022     if(tickit_term_setctl_int(self, ctl_e, value))
3023       XSRETURN_YES;
3024     else
3025       XSRETURN_NO;
3026 
3027 int
3028 setctl_str(self,ctl,value)
3029   Tickit::Term self
3030   SV          *ctl
3031   char        *value
3032   INIT:
3033     TickitTermCtl ctl_e;
3034   CODE:
3035     if(SvPOK(ctl)) {
3036       ctl_e = tickit_term_lookup_ctl(SvPV_nolen(ctl));
3037       if(ctl_e == -1)
3038         croak("Unrecognised 'ctl' name '%s'", SvPV_nolen(ctl));
3039     }
3040     else if(SvIOK(ctl))
3041       ctl_e = SvIV(ctl);
3042     else
3043       croak("Expected 'ctl' to be an integer or string");
3044     RETVAL = tickit_term_setctl_str(self, ctl_e, value);
3045   OUTPUT:
3046     RETVAL
3047 
3048 SV *
3049 getctl(self,ctl)
3050   Tickit::Term  self
3051   SV           *ctl
3052   INIT:
3053     TickitTermCtl ctl_e;
3054   CODE:
3055     if(SvPOK(ctl)) {
3056       ctl_e = tickit_term_lookup_ctl(SvPV_nolen(ctl));
3057       if(ctl_e == -1)
3058         croak("Unrecognised 'ctl' name '%s'", SvPV_nolen(ctl));
3059     }
3060     else if(SvIOK(ctl))
3061       ctl_e = SvIV(ctl);
3062     else
3063       croak("Expected 'ctl' to be an integer or string");
3064 
3065     switch(tickit_term_ctltype(ctl_e)) {
3066       case TICKIT_TYPE_BOOL: {
3067         int value;
3068         if(!tickit_term_getctl_int(self, ctl_e, &value))
3069           XSRETURN_UNDEF;
3070         RETVAL = value ? &PL_sv_yes : &PL_sv_no;
3071         break;
3072       }
3073       case TICKIT_TYPE_INT: {
3074         int value;
3075         if(!tickit_term_getctl_int(self, ctl_e, &value))
3076           XSRETURN_UNDEF;
3077         RETVAL = newSViv(value);
3078         break;
3079       }
3080 
3081       case TICKIT_TYPE_STR:
3082       case TICKIT_TYPE_NONE:
3083         RETVAL = &PL_sv_undef;
3084         break;
3085     }
3086   OUTPUT:
3087     RETVAL
3088 
3089 int
3090 setctl(self,ctl,value)
3091   Tickit::Term  self
3092   SV           *ctl
3093   SV           *value
3094   INIT:
3095     TickitTermCtl ctl_e;
3096   CODE:
3097     if(SvPOK(ctl)) {
3098       ctl_e = tickit_term_lookup_ctl(SvPV_nolen(ctl));
3099       if(ctl_e == -1)
3100         croak("Unrecognised 'ctl' name '%s'", SvPV_nolen(ctl));
3101     }
3102     else if(SvIOK(ctl))
3103       ctl_e = SvIV(ctl);
3104     else
3105       croak("Expected 'ctl' to be an integer or string");
3106 
3107     RETVAL = 0;
3108     switch(tickit_term_ctltype(ctl_e)) {
3109       case TICKIT_TYPE_BOOL:
3110       case TICKIT_TYPE_INT:
3111         RETVAL = tickit_term_setctl_int(self, ctl_e, SvIV(value));
3112         break;
3113       case TICKIT_TYPE_STR:
3114         RETVAL = tickit_term_setctl_str(self, ctl_e, SvPV_nolen(value));
3115         break;
3116       case TICKIT_TYPE_NONE:
3117         break;
3118     }
3119   OUTPUT:
3120     RETVAL
3121 
3122 void
3123 _emit_key(self,info)
3124   Tickit::Term       self
3125   Tickit::Event::Key info
3126   CODE:
3127     tickit_term_emit_key(self, info);
3128 
3129 void
3130 _emit_mouse(self,info)
3131   Tickit::Term         self
3132   Tickit::Event::Mouse info
3133   CODE:
3134     tickit_term_emit_mouse(self, info);
3135 
3136 MODULE = Tickit::Test::MockTerm    PACKAGE = Tickit::Test::MockTerm
3137 
3138 SV *
3139 _new_mocking(package,lines,cols)
3140   char *package
3141   int   lines
3142   int   cols
3143   INIT:
3144     TickitMockTerm *mt;
3145   CODE:
3146     mt = tickit_mockterm_new(lines, cols);
3147     if(!mt)
3148       XSRETURN_UNDEF;
3149 
3150     RETVAL = newSVterm_noinc((TickitTerm *)mt, "Tickit::Test::MockTerm");
3151   OUTPUT:
3152     RETVAL
3153 
3154 void
3155 get_methodlog(self)
3156   Tickit::Term self
3157   INIT:
3158     TickitMockTerm *mt;
3159     int loglen;
3160     int i;
3161   PPCODE:
3162     mt = (TickitMockTerm *)self;
3163 
3164     EXTEND(SP, (loglen = tickit_mockterm_loglen(mt)));
3165     for(i = 0; i < loglen; i++) {
3166       TickitMockTermLogEntry *entry = tickit_mockterm_peeklog(mt, i);
3167       AV *ret = newAV();
3168       switch(entry->type) {
3169       case LOG_GOTO:
3170         av_push(ret, newSVpv("goto", 0));
3171         av_push(ret, newSViv(entry->val1)); // line
3172         av_push(ret, newSViv(entry->val2)); // col
3173         break;
3174       case LOG_PRINT:
3175         av_push(ret, newSVpv("print", 0));
3176         av_push(ret, newSVpvn_utf8(entry->str, entry->val1, 1));
3177         break;
3178       case LOG_ERASECH:
3179         av_push(ret, newSVpv("erasech", 0));
3180         av_push(ret, newSViv(entry->val1)); // count
3181         av_push(ret, newSViv(entry->val2 == 1 ? 1 : 0)); // moveend
3182         break;
3183       case LOG_CLEAR:
3184         av_push(ret, newSVpv("clear", 0));
3185         break;
3186       case LOG_SCROLLRECT:
3187         av_push(ret, newSVpv("scrollrect", 0));
3188         av_push(ret, newSViv(entry->rect.top));
3189         av_push(ret, newSViv(entry->rect.left));
3190         av_push(ret, newSViv(entry->rect.lines));
3191         av_push(ret, newSViv(entry->rect.cols));
3192         av_push(ret, newSViv(entry->val1)); // downward
3193         av_push(ret, newSViv(entry->val2)); // rightward
3194         break;
3195       case LOG_SETPEN:
3196         {
3197           HV *penattrs = newHV();
3198           TickitPenAttr attr;
3199 
3200           for(attr = 1; attr < TICKIT_N_PEN_ATTRS; attr++) {
3201             const char *attrname = tickit_pen_attrname(attr);
3202             int value;
3203             if(!tickit_pen_nondefault_attr(entry->pen, attr))
3204               continue;
3205 
3206             switch(tickit_pen_attrtype(attr)) {
3207             case TICKIT_PENTYPE_BOOL:
3208               value = tickit_pen_get_bool_attr(entry->pen, attr); break;
3209             case TICKIT_PENTYPE_INT:
3210               value = tickit_pen_get_int_attr(entry->pen, attr); break;
3211             case TICKIT_PENTYPE_COLOUR:
3212               value = tickit_pen_get_colour_attr(entry->pen, attr); break;
3213             default:
3214               croak("Unreachable: unknown pen type");
3215             }
3216 
3217             sv_setiv(*hv_fetch(penattrs, attrname, strlen(attrname), 1), value);
3218           }
3219 
3220           av_push(ret, newSVpv("setpen", 0));
3221           av_push(ret, newRV_noinc((SV *)penattrs));
3222         }
3223         break;
3224       }
3225       mPUSHs(newRV_noinc((SV *)ret));
3226     }
3227 
3228     tickit_mockterm_clearlog(mt);
3229 
3230     XSRETURN(i);
3231 
3232 SV *
3233 get_display_text(self,line,col,width)
3234   Tickit::Term self
3235   int line
3236   int col
3237   int width
3238   INIT:
3239     STRLEN len;
3240   CODE:
3241     len = tickit_mockterm_get_display_text((TickitMockTerm *)self, NULL, 0, line, col, width);
3242 
3243     RETVAL = newSV(len+1);
3244 
3245     tickit_mockterm_get_display_text((TickitMockTerm *)self, SvPVX(RETVAL), len, line, col, width);
3246 
3247     SvPOK_on(RETVAL);
3248     SvUTF8_on(RETVAL);
3249     SvCUR_set(RETVAL, len);
3250   OUTPUT:
3251     RETVAL
3252 
3253 SV *
3254 get_display_pen(self,line,col)
3255   Tickit::Term self
3256   int line
3257   int col
3258   INIT:
3259     TickitPen *pen;
3260     HV *penattrs;
3261     TickitPenAttr attr;
3262   CODE:
3263     pen = tickit_mockterm_get_display_pen((TickitMockTerm *)self, line, col);
3264 
3265     penattrs = newHV();
3266     for(attr = 1; attr < TICKIT_N_PEN_ATTRS; attr++) {
3267       const char *attrname;
3268       if(!tickit_pen_nondefault_attr(pen, attr))
3269         continue;
3270 
3271       attrname = tickit_pen_attrname(attr);
3272       hv_store(penattrs, attrname, strlen(attrname), pen_get_attr(pen, attr), 0);
3273     }
3274 
3275     RETVAL = newRV_noinc((SV *)penattrs);
3276   OUTPUT:
3277     RETVAL
3278 
3279 void
3280 resize(self,newlines,newcols)
3281   Tickit::Term self
3282   int newlines
3283   int newcols
3284   CODE:
3285     tickit_mockterm_resize((TickitMockTerm *)self, newlines, newcols);
3286 
3287 int
3288 line(self)
3289   Tickit::Term self
3290   ALIAS:
3291     line        = 0
3292     col         = 1
3293     cursorvis   = 2
3294     cursorshape = 3
3295   INIT:
3296     TickitMockTerm *mt;
3297   CODE:
3298     mt = (TickitMockTerm *)self;
3299     switch(ix) {
3300       case 0: tickit_mockterm_get_position(mt, &RETVAL, NULL); break;
3301       case 1: tickit_mockterm_get_position(mt, NULL, &RETVAL); break;
3302       case 2: tickit_term_getctl_int(self, TICKIT_TERMCTL_CURSORVIS, &RETVAL); break;
3303       case 3: tickit_term_getctl_int(self, TICKIT_TERMCTL_CURSORSHAPE, &RETVAL); break;
3304     }
3305   OUTPUT:
3306     RETVAL
3307 
3308 MODULE = Tickit             PACKAGE = Tickit::Utils
3309 
3310 size_t
3311 string_count(str,pos,limit=NULL)
3312     SV *str
3313     Tickit::StringPos pos
3314     Tickit::StringPos limit
3315   INIT:
3316     char *s;
3317     STRLEN len;
3318   CODE:
3319     if(!SvUTF8(str)) {
3320       str = sv_mortalcopy(str);
3321       sv_utf8_upgrade(str);
3322     }
3323 
3324     s = SvPVutf8(str, len);
3325     RETVAL = tickit_utf8_ncount(s, len, pos, limit);
3326     if(RETVAL == -1)
3327       XSRETURN_UNDEF;
3328   OUTPUT:
3329     RETVAL
3330 
3331 size_t
3332 string_countmore(str,pos,limit=NULL)
3333     SV *str
3334     Tickit::StringPos pos
3335     Tickit::StringPos limit
3336   INIT:
3337     char *s;
3338     STRLEN len;
3339   CODE:
3340     if(!SvUTF8(str)) {
3341       str = sv_mortalcopy(str);
3342       sv_utf8_upgrade(str);
3343     }
3344 
3345     s = SvPVutf8(str, len);
3346     RETVAL = tickit_utf8_ncountmore(s, len, pos, limit);
3347     if(RETVAL == -1)
3348       XSRETURN_UNDEF;
3349   OUTPUT:
3350     RETVAL
3351 
3352 int textwidth(str)
3353     SV *str
3354   INIT:
3355     STRLEN len;
3356     const char *s;
3357     TickitStringPos pos, limit = INIT_TICKIT_STRINGPOS_LIMIT_NONE;
3358 
3359   CODE:
3360     RETVAL = 0;
3361 
3362     if(!SvUTF8(str)) {
3363       str = sv_mortalcopy(str);
3364       sv_utf8_upgrade(str);
3365     }
3366 
3367     s = SvPVutf8(str, len);
3368     if(tickit_utf8_ncount(s, len, &pos, &limit) == -1)
3369       XSRETURN_UNDEF;
3370 
3371     RETVAL = pos.columns;
3372 
3373   OUTPUT:
3374     RETVAL
3375 
3376 void chars2cols(str,...)
3377     SV *str;
3378   INIT:
3379     STRLEN len;
3380     const char *s;
3381     int i;
3382     TickitStringPos pos, limit;
3383     size_t bytes;
3384 
3385   PPCODE:
3386     if(!SvUTF8(str)) {
3387       str = sv_mortalcopy(str);
3388       sv_utf8_upgrade(str);
3389     }
3390 
3391     s = SvPVutf8(str, len);
3392 
3393     EXTEND(SP, items - 1);
3394 
3395     tickit_stringpos_zero(&pos);
3396     tickit_stringpos_limit_bytes(&limit, len);
3397 
3398     for(i = 1; i < items; i++ ) {
3399       limit.codepoints = SvUV(ST(i));
3400       if(limit.codepoints < pos.codepoints)
3401         croak("chars2cols requires a monotonically-increasing list of character numbers; %d is not greater than %d\n",
3402           limit.codepoints, pos.codepoints);
3403 
3404       bytes = tickit_utf8_ncountmore(s, len, &pos, &limit);
3405       if(bytes == -1)
3406         XSRETURN_UNDEF;
3407 
3408       mPUSHu(pos.columns);
3409 
3410       if(GIMME_V != G_ARRAY)
3411         XSRETURN(1);
3412     }
3413 
3414     XSRETURN(items - 1);
3415 
3416 void cols2chars(str,...)
3417     SV *str;
3418   INIT:
3419     STRLEN len;
3420     const char *s;
3421     int i;
3422     TickitStringPos pos, limit;
3423     size_t bytes;
3424 
3425   PPCODE:
3426     if(!SvUTF8(str)) {
3427       str = sv_mortalcopy(str);
3428       sv_utf8_upgrade(str);
3429     }
3430 
3431     s = SvPVutf8(str, len);
3432 
3433     EXTEND(SP, items - 1);
3434 
3435     tickit_stringpos_zero(&pos);
3436     tickit_stringpos_limit_bytes(&limit, len);
3437 
3438     for(i = 1; i < items; i++ ) {
3439       limit.columns = SvUV(ST(i));
3440       if(limit.columns < pos.columns)
3441         croak("cols2chars requires a monotonically-increasing list of column numbers; %d is not greater than %d\n",
3442           limit.columns, pos.columns);
3443 
3444       bytes = tickit_utf8_ncountmore(s, len, &pos, &limit);
3445       if(bytes == -1)
3446         XSRETURN_UNDEF;
3447 
3448       mPUSHu(pos.codepoints);
3449 
3450       if(GIMME_V != G_ARRAY)
3451         XSRETURN(1);
3452     }
3453 
3454     XSRETURN(items - 1);
3455 
3456 MODULE = Tickit  PACKAGE = Tickit::Window
3457 
3458 SV *
3459 _new_root(package,tt,tickit)
3460   char         *package
3461   Tickit::Term  tt
3462   SV           *tickit
3463   INIT:
3464     Tickit__Window  self;
3465     TickitWindow   *win;
3466   CODE:
3467     win = tickit_window_new_root(tt);
3468     if(!win)
3469       XSRETURN_UNDEF;
3470 
3471     RETVAL = newSVwin_noinc(win);
3472     self = INT2PTR(struct Tickit__Window *, SvIV(SvRV(RETVAL)));
3473 
3474     self->tickit = newSVsv(tickit);
3475     sv_rvweaken(self->tickit);
3476   OUTPUT:
3477     RETVAL
3478 
3479 SV *
3480 _make_sub(win,top,left,lines,cols,flags)
3481   Tickit::Window win;
3482   int            top;
3483   int            left;
3484   int            lines;
3485   int            cols;
3486   int            flags;
3487   INIT:
3488     TickitRect rect;
3489     TickitWindow *subwin;
3490   CODE:
3491     rect.top   = top;
3492     rect.left  = left;
3493     rect.lines = lines;
3494     rect.cols  = cols;
3495     subwin = tickit_window_new(win->win, rect, flags);
3496     if(!subwin)
3497       XSRETURN_UNDEF;
3498 
3499     /* parent window holds a reference, we have another */
3500     RETVAL = newSVwin(subwin);
3501   OUTPUT:
3502     RETVAL
3503 
3504 void
3505 DESTROY(self)
3506   Tickit::Window self
3507   CODE:
3508     tickit_window_unref(self->win);
3509     self->win = NULL;
3510 
3511 void
3512 close(self)
3513   Tickit::Window self
3514   CODE:
3515     tickit_window_close(self->win);
3516 
3517 int
3518 top(self)
3519   Tickit::Window self
3520   CODE:
3521     RETVAL = tickit_window_top(self->win);
3522   OUTPUT:
3523     RETVAL
3524 
3525 int
3526 left(self)
3527   Tickit::Window self
3528   CODE:
3529     RETVAL = tickit_window_left(self->win);
3530   OUTPUT:
3531     RETVAL
3532 
3533 int
3534 lines(self)
3535   Tickit::Window self
3536   CODE:
3537     RETVAL = tickit_window_lines(self->win);
3538   OUTPUT:
3539     RETVAL
3540 
3541 int
3542 cols(self)
3543   Tickit::Window self
3544   CODE:
3545     RETVAL = tickit_window_cols(self->win);
3546   OUTPUT:
3547     RETVAL
3548 
3549 int
3550 abs_top(self)
3551   Tickit::Window self
3552   CODE:
3553     RETVAL = tickit_window_get_abs_geometry(self->win).top;
3554   OUTPUT:
3555     RETVAL
3556 
3557 int
3558 abs_left(self)
3559   Tickit::Window self
3560   CODE:
3561     RETVAL = tickit_window_get_abs_geometry(self->win).left;
3562   OUTPUT:
3563     RETVAL
3564 
3565 SV *
3566 root(self)
3567   Tickit::Window self
3568   ALIAS:
3569     root   = 0
3570     parent = 1
3571     term   = 2
3572     _tickit = 3
3573   CODE:
3574     switch(ix) {
3575       case 0: RETVAL = newSVwin(tickit_window_root(self->win)); break;
3576       case 1: {
3577         TickitWindow *parent = tickit_window_parent(self->win);
3578         RETVAL = parent ? newSVwin(parent) : &PL_sv_undef;
3579         break;
3580       }
3581       case 2: RETVAL = newSVterm(tickit_window_get_term(self->win), "Tickit::Term"); break;
3582       case 3: {
3583         RETVAL = self->tickit ? newSVsv(self->tickit) : &PL_sv_undef;
3584         break;
3585       }
3586       default: croak("Unreachable");
3587     }
3588   OUTPUT:
3589     RETVAL
3590 
3591 void
3592 subwindows(self)
3593   Tickit::Window self
3594   INIT:
3595     size_t n;
3596     TickitWindow **children;
3597     size_t i;
3598   PPCODE:
3599     n = tickit_window_children(self->win);
3600 
3601     if(GIMME_V != G_ARRAY) {
3602       mPUSHi(n);
3603       XSRETURN(1);
3604     }
3605 
3606     Newx(children, n, TickitWindow *);
3607     tickit_window_get_children(self->win, children, n);
3608 
3609     EXTEND(SP, n);
3610     for(i = 0; i < n; i++) {
3611       mPUSHs(newSVwin(children[i]));
3612     }
3613 
3614     Safefree(children);
3615 
3616     XSRETURN(n);
3617 
3618 int
3619 _bind_event(self,ev,flags,code,data = &PL_sv_undef)
3620   Tickit::Window  self
3621   char           *ev
3622   int             flags
3623   CV             *code
3624   SV             *data
3625   INIT:
3626     TickitWindowEvent _ev = -1;
3627     struct GenericEventData *user;
3628   CODE:
3629     switch(ev[0]) {
3630       case 'e':
3631         if(strEQ(ev, "expose"))
3632           _ev = TICKIT_WINDOW_ON_EXPOSE;
3633         break;
3634       case 'f':
3635         if(strEQ(ev, "focus"))
3636           _ev = TICKIT_WINDOW_ON_FOCUS;
3637         break;
3638       case 'g':
3639         if(strEQ(ev, "geomchange"))
3640           _ev = TICKIT_WINDOW_ON_GEOMCHANGE;
3641         break;
3642       case 'k':
3643         if(strEQ(ev, "key"))
3644           _ev = TICKIT_WINDOW_ON_KEY;
3645         break;
3646       case 'm':
3647         if(strEQ(ev, "mouse"))
3648           _ev = TICKIT_WINDOW_ON_MOUSE;
3649         break;
3650     }
3651     if(_ev == -1)
3652       croak("Unrecognised event name '%s'", ev);
3653 
3654     user = new_eventdata(_ev, newSVsv(data), code);
3655     user->self = newSVsv(ST(0));
3656 
3657     sv_rvweaken(user->self);
3658 
3659     RETVAL = tickit_window_bind_event(self->win, _ev, flags|TICKIT_BIND_UNBIND, window_userevent_fn, user);
3660   OUTPUT:
3661     RETVAL
3662 
3663 void
3664 unbind_event_id(self,id)
3665   Tickit::Window self
3666   int            id
3667   CODE:
3668     tickit_window_unbind_event_id(self->win, id);
3669 
3670 void
3671 flush(self)
3672   Tickit::Window  self
3673   CODE:
3674     tickit_window_flush(self->win);
3675 
3676 void
3677 expose(self,rect = NULL)
3678   Tickit::Window     self
3679   Tickit::Rect_MAYBE rect
3680   CODE:
3681     tickit_window_expose(self->win, rect);
3682 
3683 void
3684 hide(self)
3685   Tickit::Window  self
3686   CODE:
3687     tickit_window_hide(self->win);
3688 
3689 void
3690 show(self)
3691   Tickit::Window  self
3692   CODE:
3693     tickit_window_show(self->win);
3694 
3695 void
3696 resize(self,lines,cols)
3697   Tickit::Window self
3698   int            lines
3699   int            cols
3700   CODE:
3701     tickit_window_resize(self->win, lines, cols);
3702 
3703 void
3704 reposition(self,top,left)
3705   Tickit::Window self
3706   int            top
3707   int            left
3708   CODE:
3709     tickit_window_reposition(self->win, top, left);
3710 
3711 void
3712 change_geometry(self,top,left,lines,cols)
3713   Tickit::Window  self
3714   int             top
3715   int             left
3716   int             lines
3717   int             cols
3718   INIT:
3719     TickitRect rect;
3720   CODE:
3721     rect.top   = top;
3722     rect.left  = left;
3723     rect.lines = lines;
3724     rect.cols  = cols;
3725     tickit_window_set_geometry(self->win, rect);
3726 
3727 bool
3728 is_visible(self)
3729   Tickit::Window  self
3730   CODE:
3731     RETVAL = tickit_window_is_visible(self->win);
3732   OUTPUT:
3733     RETVAL
3734 
3735 SV *
3736 pen(self)
3737   Tickit::Window  self
3738   CODE:
3739     RETVAL = newSVpen(tickit_window_get_pen(self->win), "Tickit::Pen::Mutable");
3740   OUTPUT:
3741     RETVAL
3742 
3743 void
3744 set_pen(self,pen)
3745   Tickit::Window  self
3746   Tickit::Pen     pen
3747   CODE:
3748     tickit_window_set_pen(self->win, pen);
3749 
3750 void
3751 raise(self)
3752   Tickit::Window self
3753   ALIAS:
3754     raise = 0
3755     lower = 1
3756     raise_to_front = 2
3757     lower_to_back  = 3
3758   CODE:
3759     switch(ix) {
3760       case 0: tickit_window_raise(self->win); break;
3761       case 1: tickit_window_lower(self->win); break;
3762       case 2: tickit_window_raise_to_front(self->win); break;
3763       case 3: tickit_window_lower_to_back(self->win); break;
3764     }
3765 
3766 bool
3767 _scrollrect(self,rect,downward,rightward,pen)
3768   Tickit::Window self
3769   Tickit::Rect   rect
3770   int            downward
3771   int            rightward
3772   Tickit::Pen    pen
3773   CODE:
3774     RETVAL = tickit_window_scrollrect(self->win, rect, downward, rightward, pen);
3775   OUTPUT:
3776     RETVAL
3777 
3778 bool
3779 _scroll_with_children(self,downward,rightward)
3780   Tickit::Window self
3781   int            downward
3782   int            rightward
3783   CODE:
3784     RETVAL = tickit_window_scroll_with_children(self->win, downward, rightward);
3785   OUTPUT:
3786     RETVAL
3787 
3788 bool
3789 is_focused(self)
3790   Tickit::Window  self
3791   CODE:
3792     RETVAL = tickit_window_is_focused(self->win);
3793   OUTPUT:
3794     RETVAL
3795 
3796 void
3797 take_focus(self)
3798   Tickit::Window  self
3799   CODE:
3800     tickit_window_take_focus(self->win);
3801 
3802 void
3803 set_cursor_position(self,line,col)
3804   Tickit::Window  self
3805   int             line
3806   int             col
3807   CODE:
3808     tickit_window_set_cursor_position(self->win, line, col);
3809 
3810 SV *
3811 getctl(self,ctl)
3812   Tickit::Window  self
3813   SV             *ctl
3814   INIT:
3815     TickitWindowCtl ctl_e;
3816   CODE:
3817     if(SvPOK(ctl)) {
3818       ctl_e = tickit_window_lookup_ctl(SvPV_nolen(ctl));
3819       if(ctl_e == -1)
3820         croak("Unrecognised 'ctl' name '%s'", SvPV_nolen(ctl));
3821     }
3822     else if(SvIOK(ctl))
3823       ctl_e = SvIV(ctl);
3824     else
3825       croak("Expected 'ctl' to be an integer or string");
3826 
3827     switch(tickit_window_ctltype(ctl_e)) {
3828       case TICKIT_TYPE_BOOL: {
3829         int value;
3830         if(!tickit_window_getctl_int(self->win, ctl_e, &value))
3831           XSRETURN_UNDEF;
3832         RETVAL = value ? &PL_sv_yes : &PL_sv_no;
3833         break;
3834       }
3835       case TICKIT_TYPE_INT: {
3836         int value;
3837         if(!tickit_window_getctl_int(self->win, ctl_e, &value))
3838           XSRETURN_UNDEF;
3839         RETVAL = newSViv(value);
3840         break;
3841       }
3842 
3843       case TICKIT_TYPE_STR:
3844       case TICKIT_TYPE_NONE:
3845         RETVAL = &PL_sv_undef;
3846         break;
3847     }
3848   OUTPUT:
3849     RETVAL
3850 
3851 
3852 int
3853 setctl(self,ctl,value)
3854   Tickit::Window  self
3855   SV             *ctl
3856   SV             *value
3857   INIT:
3858     TickitWindowCtl ctl_e;
3859   CODE:
3860     if(SvPOK(ctl)) {
3861       ctl_e = tickit_window_lookup_ctl(SvPV_nolen(ctl));
3862       if(ctl_e == -1)
3863         croak("Unrecognised 'ctl' name '%s'", SvPV_nolen(ctl));
3864     }
3865     else if(SvIOK(ctl))
3866       ctl_e = SvIV(ctl);
3867     else
3868       croak("Expected 'ctl' to be an integer or string");
3869 
3870     RETVAL = 0;
3871     switch(tickit_window_ctltype(ctl_e)) {
3872       case TICKIT_TYPE_BOOL:
3873       case TICKIT_TYPE_INT:
3874         RETVAL = tickit_window_setctl_int(self->win, ctl_e, SvIV(value));
3875         break;
3876       case TICKIT_TYPE_STR:
3877         // TODO: currently there aren't any
3878         break;
3879       case TICKIT_TYPE_NONE:
3880         break;
3881     }
3882   OUTPUT:
3883     RETVAL
3884 
3885 MODULE = Tickit  PACKAGE = Tickit::_Tickit
3886 
3887 SV *
3888 new(package,term)
3889   char               *package
3890   Tickit::Term_MAYBE  term
3891   INIT:
3892     struct TickitBuilder builder = { 0 };
3893     Tickit *t;
3894   CODE:
3895     if(term)
3896       builder.tt = tickit_term_ref(term);
3897     else
3898       builder.term_builder.open = TICKIT_OPEN_STDIO;
3899 
3900     t = tickit_build(&builder);
3901     if(!t)
3902       XSRETURN_UNDEF;
3903     RETVAL = newSV(0);
3904     sv_setref_pv(RETVAL, package, t);
3905   OUTPUT:
3906     RETVAL
3907 
3908 SV *
3909 _new_with_evloop(package, term, ...)
3910   char *package
3911   SV   *term
3912   INIT:
3913     TickitTerm *tt = NULL;
3914     struct TickitBuilder builder = { 0 };
3915     Tickit *t;
3916     EventLoopData *evdata;
3917   CODE:
3918     /* A not-actually-documented API to wrap the event hook binding function
3919      *   tickit_new_with_evloop(3)
3920      * This is for use by Tickit::Async, POEx::Tickit, and maybe others
3921      */
3922     if(!term || !SvOK(term))
3923       tt = NULL;
3924     else if(SvROK(term) && sv_derived_from(term, "Tickit::Term"))
3925       tt = INT2PTR(TickitTerm *, SvIV((SV*)SvRV(term)));
3926     else
3927       Perl_croak(aTHX_ "term is not of type Tickit::Term");
3928 
3929     if(tt)
3930       builder.tt = tickit_term_ref(tt);
3931     else
3932       builder.term_builder.open = TICKIT_OPEN_STDIO;
3933 
3934     Newx(evdata, 1, EventLoopData);
3935     Zero(evdata, 1, EventLoopData);
3936 #ifdef tTHX
3937     evdata->myperl = aTHX;
3938 #endif
3939     if(!SvROK(ST(2))) {
3940       U32 idx = 2;
3941       while(idx < items) {
3942         SV *namesv = ST(idx++);
3943         const char *name = SvPVbyte_nolen(namesv);
3944         CV *code = (CV *)SvREFCNT_inc((SV *)cv_from_sv(ST(idx++), name));
3945 
3946         if(strEQ(name, "init"))
3947           evdata->cb_init = code;
3948         else if(strEQ(name, "destroy"))
3949           evdata->cb_destroy = code;
3950         else if(strEQ(name, "run"))
3951           evdata->cb_run = code;
3952         else if(strEQ(name, "stop"))
3953           evdata->cb_stop = code;
3954         else if(strEQ(name, "io"))
3955           evdata->cb_io = code;
3956         else if(strEQ(name, "cancel_io"))
3957           evdata->cb_cancel_io = code;
3958         else if(strEQ(name, "timer"))
3959           evdata->cb_timer = code;
3960         else if(strEQ(name, "cancel_timer"))
3961           evdata->cb_cancel_timer = code;
3962         else if(strEQ(name, "later"))
3963           evdata->cb_later = code;
3964         else if(strEQ(name, "cancel_later"))
3965           evdata->cb_cancel_later = code;
3966         else if(strEQ(name, "signal"))
3967           evdata->cb_signal = code;
3968         else if(strEQ(name, "cancel_signal"))
3969           evdata->cb_cancel_signal = code;
3970         else if(strEQ(name, "process"))
3971           evdata->cb_process = code;
3972         else if(strEQ(name, "cancel_process"))
3973           evdata->cb_cancel_process = code;
3974         else
3975           croak("Unrecognised evloop callback name %s", name);
3976       }
3977 
3978       /* The first 6 are required */
3979       if(!evdata->cb_init)
3980         croak("Required evloop callback 'init' is missing");
3981       if(!evdata->cb_destroy)
3982         croak("Required evloop callback 'destroy' is missing");
3983       if(!evdata->cb_run)
3984         croak("Required evloop callback 'run' is missing");
3985       if(!evdata->cb_stop)
3986         croak("Required evloop callback 'stop' is missing");
3987       if(!evdata->cb_io)
3988         croak("Required evloop callback 'io' is missing");
3989       if(!evdata->cb_cancel_io)
3990         croak("Required evloop callback 'cancel_io' is missing");
3991       /* The rest are optional */
3992     }
3993     else {
3994       U32 idx = 2;
3995 
3996       /* likely a CODE ref; we'll do this old-style positional */
3997       evdata->cb_init         = (CV *)SvREFCNT_inc((SV *)cv_from_sv(ST(idx++), "init"));
3998       evdata->cb_destroy      = (CV *)SvREFCNT_inc((SV *)cv_from_sv(ST(idx++), "destroy"));
3999       evdata->cb_run          = (CV *)SvREFCNT_inc((SV *)cv_from_sv(ST(idx++), "run"));
4000       evdata->cb_stop         = (CV *)SvREFCNT_inc((SV *)cv_from_sv(ST(idx++), "stop"));
4001       evdata->cb_io           = (CV *)SvREFCNT_inc((SV *)cv_from_sv(ST(idx++), "io"));
4002       evdata->cb_cancel_io    = (CV *)SvREFCNT_inc((SV *)cv_from_sv(ST(idx++), "cancel_io"));
4003       evdata->cb_timer        = (CV *)SvREFCNT_inc((SV *)cv_from_sv(ST(idx++), "timer"));
4004       evdata->cb_cancel_timer = (CV *)SvREFCNT_inc((SV *)cv_from_sv(ST(idx++), "cancel_timer"));
4005       evdata->cb_later        = (CV *)SvREFCNT_inc((SV *)cv_from_sv(ST(idx++), "later"));
4006       evdata->cb_cancel_later = (CV *)SvREFCNT_inc((SV *)cv_from_sv(ST(idx++), "cancel_later"));
4007     }
4008 
4009     /* Uses the not-technically-documented evhooks / evinitdata TickitBuilder fields */
4010     builder.evhooks = &evhooks;
4011     builder.evinitdata = evdata;
4012 
4013     t = tickit_build(&builder);
4014     if(!t)
4015       XSRETURN_UNDEF;
4016 
4017     RETVAL = newSV(0);
4018     sv_setref_pv(RETVAL, package, t);
4019   OUTPUT:
4020     RETVAL
4021 
4022 void
4023 DESTROY(self)
4024   Tickit::_Tickit self
4025   CODE:
4026     tickit_unref(self);
4027 
4028 SV *
4029 rootwin(self,tickit)
4030   Tickit::_Tickit  self
4031   SV              *tickit
4032   INIT:
4033     Tickit__Window win;
4034   CODE:
4035     RETVAL = newSVwin(tickit_get_rootwin(self));
4036     win = INT2PTR(struct Tickit__Window *, SvIV(SvRV(RETVAL)));
4037     if(!win->tickit) {
4038       win->tickit = newSVsv(tickit);
4039       sv_rvweaken(win->tickit);
4040     }
4041   OUTPUT:
4042     RETVAL
4043 
4044 SV *
4045 term(self)
4046   Tickit::_Tickit self
4047   CODE:
4048     RETVAL = newSVterm(tickit_get_term(self), "Tickit::Term");
4049   OUTPUT:
4050     RETVAL
4051 
4052 bool
4053 setctl(self, ctl, value)
4054   Tickit::_Tickit  self
4055   SV              *ctl
4056   SV              *value
4057   INIT:
4058     TickitCtl ctl_e;
4059   CODE:
4060     if(SvPOK(ctl)) {
4061       ctl_e = tickit_lookup_ctl(SvPV_nolen(ctl));
4062       if(ctl_e == -1)
4063         croak("Unrecognised 'ctl' name '%s'", SvPV_nolen(ctl));
4064     }
4065     else if(SvIOK(ctl))
4066       ctl_e = SvIV(ctl);
4067     else
4068       croak("Expected 'ctl' to be an integer or string");
4069 
4070     RETVAL = 0;
4071     switch(tickit_ctltype(ctl_e)) {
4072       case TICKIT_TYPE_BOOL:
4073       case TICKIT_TYPE_INT:
4074         RETVAL = tickit_setctl_int(self, ctl_e, SvIV(value));
4075         break;
4076       case TICKIT_TYPE_STR:
4077       case TICKIT_TYPE_NONE:
4078         break;
4079     }
4080   OUTPUT:
4081     RETVAL
4082 
4083 UV
4084 watch_io(self, fd, cond, code)
4085   Tickit::_Tickit  self
4086   UV               fd
4087   UV               cond
4088   CV              *code
4089   CODE:
4090     RETVAL = PTR2UV(tickit_watch_io(self, fd, cond, TICKIT_BIND_UNBIND, invoke_iocallback,
4091       new_eventdata_codeonly(code)));
4092   OUTPUT:
4093     RETVAL
4094 
4095 UV
4096 watch_timer_after(self, delay, code)
4097   Tickit::_Tickit  self
4098   NV               delay
4099   CV              *code
4100   INIT:
4101     struct timeval after;
4102   CODE:
4103     /* For convenience of the calling Perl code we take fractional seconds and
4104      * convert to struct timeval here.
4105      */
4106     after.tv_sec = (long)delay;
4107     after.tv_usec = (delay - after.tv_sec) * 1000000;
4108 
4109     RETVAL = PTR2UV(tickit_watch_timer_after_tv(self, &after, TICKIT_BIND_UNBIND, invoke_callback,
4110       new_eventdata_codeonly(code)));
4111   OUTPUT:
4112     RETVAL
4113 
4114 UV
4115 watch_timer_at(self, epoch, code)
4116   Tickit::_Tickit  self
4117   NV               epoch
4118   CV              *code
4119   INIT:
4120     struct timeval at;
4121   CODE:
4122     /* For convenience of the calling Perl code we take fractional seconds and
4123      * convert to struct timeval here.
4124      */
4125     at.tv_sec = (long)epoch;
4126     at.tv_usec = (epoch - at.tv_sec) * 1000000;
4127 
4128     RETVAL = PTR2UV(tickit_watch_timer_at_tv(self, &at, TICKIT_BIND_UNBIND, invoke_callback,
4129       new_eventdata_codeonly(code)));
4130   OUTPUT:
4131     RETVAL
4132 
4133 UV
4134 watch_signal(self, signum, code)
4135   Tickit::_Tickit  self
4136   int              signum
4137   CV              *code
4138   CODE:
4139     RETVAL = PTR2UV(tickit_watch_signal(self, signum, TICKIT_BIND_UNBIND, invoke_callback,
4140       new_eventdata_codeonly(code)));
4141   OUTPUT:
4142     RETVAL
4143 
4144 UV
4145 watch_process(self, pid, code)
4146   Tickit::_Tickit  self
4147   IV               pid
4148   CV              *code
4149   CODE:
4150     RETVAL = PTR2UV(tickit_watch_process(self, pid, TICKIT_BIND_UNBIND, invoke_processcallback,
4151       new_eventdata_codeonly(code)));
4152   OUTPUT:
4153     RETVAL
4154 
4155 void
4156 watch_cancel(self, id)
4157   Tickit::_Tickit  self
4158   UV               id
4159   CODE:
4160     tickit_watch_cancel(self, INT2PTR(void *,id));
4161 
4162 UV
4163 watch_later(self, code)
4164   Tickit::_Tickit  self
4165   CV              *code
4166   CODE:
4167     RETVAL = PTR2UV(tickit_watch_later(self, TICKIT_BIND_UNBIND, invoke_callback,
4168       new_eventdata_codeonly(code)));
4169   OUTPUT:
4170     RETVAL
4171 
4172 void
4173 run(self)
4174   Tickit::_Tickit  self
4175   CODE:
4176     tickit_run(self);
4177 
4178 void
4179 stop(self)
4180   Tickit::_Tickit  self
4181   CODE:
4182     tickit_stop(self);
4183 
4184 void
4185 tick(self, flags=0)
4186   Tickit::_Tickit  self
4187   int              flags
4188   CODE:
4189     tickit_tick(self, flags);
4190 
4191 MODULE = Tickit  PACKAGE = Tickit
4192 
4193 int
4194 version_major()
4195   ALIAS:
4196     version_major = 0
4197     version_minor = 1
4198     version_patch = 2
4199   CODE:
4200     switch(ix) {
4201       case 0: RETVAL = tickit_version_major(); break;
4202       case 1: RETVAL = tickit_version_minor(); break;
4203       case 2: RETVAL = tickit_version_patch(); break;
4204     }
4205   OUTPUT:
4206     RETVAL
4207 
4208 BOOT:
4209   /* Check libtickit version */
4210   if(tickit_version_major() != TICKIT_VERSION_MAJOR ||
4211      tickit_version_minor() != TICKIT_VERSION_MINOR ||
4212      tickit_version_patch() <  TICKIT_VERSION_PATCH) {
4213     croak("libtickit version mismatch: compiled for version %d.%d.%d, running with %d.%d.%d\n",
4214       TICKIT_VERSION_MAJOR, TICKIT_VERSION_MINOR, TICKIT_VERSION_PATCH,
4215       tickit_version_major(), tickit_version_minor(), tickit_version_patch());
4216   }
4217 
4218   S_setup_constants(aTHX);
4219