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