libtickit-0.3.4/CHANGES0000644000000000000000000000771213613430267012631 0ustar 000000000000002020-01-27 00:25 0.3.4 * Added macros and functions for version query * Ensure that all kinds of event watches can be cancelled, even lone ones * Fix name of tickit_watch_timer_at_epoch() function * Fix manpages around recent function renames * Added manpage documentation on behaviour of event watches at cancel time 2019-11-18 17:05 0.3.3 * Ensure that calling tickit_window_close(3) twice doesn't crash (thanks noctux) * Conditionally #define feature-test macros only on __GLIBC__ because other OSes (e.g. the BSDs) use those macros to hide symbols (LP1824791) * Make sure that examples build with $(CC) rather than hardcoded gcc * Account for X10's lack of mouse button reporting on release events * Avoid SEGV on shutdown if windows get closed after their parent 2019-04-12 16:35 0.3.2 * #define _POSIX_C_SOURCE as some platforms need it * Hide terminal cursor around redraws * Fix bug in terminal output buffering logic around split writes * Default 4096 byte output buffer in toplevel Tickit instance * Skip screen-based terminfo test if loading terminfo fails, due to ongoing unibilium issue 2019-04-10 21:03 0.3.1 * Avoid nested functions in unit tests as some versions of gcc dislike this * Bugfixes and unit test for ti_hook * Fix various (harmless) warnings from noisy C compilers * Use only one standard streq() macro across all source * Fix signature of render() in examples/demo-scroll.c * Fix documentation of tickit_utf8_ncountmore(3) to include len param (LP1797763) * Have TICKIT_TERMCTL_COLORS return (1<<24) on RGB8-capable terminals 2019-03-17 22:11 0.3 * Renamed: + tickit_timer_after_msec => tickit_watch_timer_after_msec + tickit_timer_after_tv => tickit_watch_timer_after_tv + tickit_later => tickit_watch_later + tickit_timer_cancel => tickit_watch_cancel * Added toplevel Tickit functions for loop control + tickit_tick(3) + tickit_watch_io_read(3) + tickit_watch_timer_at_epoch(3) + tickit_watch_timer_at_tv(3) * Added TickitType + tickit_term_ctltype(3) * Added window controls + tickit_window_getctl_int(3) + tickit_window_setctl_int(3) + tickit_window_ctlname(3) + tickit_window_lookup_ctl(3) + tickit_window_ctltype(3) * Added toplevel Tickit instance controls + tickit_getctl_int(3) + tickit_setctl_int(3) + tickit_ctlname(3) + tickit_lookup_ctl(3) + tickit_ctltype(3) * TICKIT_PEN_UNDER type is now integer, supports single, double, wavy underline * Fixed various example/demo-*.c files so they work again * Added experimental new ability to provide event loop hooks for other event loops * Deleted the deprecated tickit_string_* functions that were renamed to tickit_utf8_* * Renumbered TICKIT_PEN_* constants to start from 1; lists can end with 0 * Use termios to know what backspace (VERASE) character is, rather than relying on terminfo - experience from vim, neovim, etc.. is that this is often more reliable * tickit_watch_* callbacks now receive an info pointer, to match the calling style of other object bindings 2018-01-05 14:47 0.2 * Added tickit_window_{is,set}_steal_focus(3) * Added entire toplevel Tickit instance * New bind_events API style - one event + flags, instead of bitmask * Renamed: + tickit_string_seqlen => tickit_utf8_seqlen + tickit_string_putchar => tickit_utf8_put + tickit_string_mbswidth => tickit_utf8_mbswidth + tickit_string_byte2col => tickit_utf8_byte2col + tickit_string_col2byte => tickit_utf8_col2byte + tickit_string_count* => tickit_utf8_count* * Added TickitString * Added tickit_rectset_get_rect(3) * Added tickit_renderbuffer_skiprect(3) * Added tickit_renderbuffer_moverect(3) * Added tickit_term_ctlname(3) and tickit_term_lookup_ctl(3) * Added tickit_term_pause(3), tickit_term_resume(3) * Added secondary RGB8 pen attributes * Support RGB8 in xterm driver libtickit-0.3.4/CODE-MAP0000644000000000000000000000475713613430267012714 0ustar 00000000000000CHANGES - the changelog CODE-MAP - high-level list and description of files in the repository CREDITS - list of other contributors to the code LICENSE - legalese Makefile - main build file examples/ - contains standalone example programs demonstrating parts of API include/tickit.h - main include file include/tickit-evloop.h - include file for implementing custom event loops include/tickit-mockterm.h - include file for tests using the mock terminal include/tickit-termdrv.h - include file for terminal drivers man/ - contains the documentation manpages man/also - lists aliases for manpages; used to create symlinks at install time src/ - contains the source code for the library src/bindings.c - (internal) implementation of lists of event handlers src/bindings.h - header file for lists of event handlers src/debug.c - helper functions for printing debug log src/evloop-default.c - implements the default event loop used by the toplevel Tickit instance src/linechars.inc.PL - script to generate `linechars.inc` src/linechars.inc - defines an array of unicode codepoints to use in linedrawing src/mockterm.c - a virtual TickitTerm implementation that supports query; used for unit tests src/pen.c - implements TickitPen; stores a collection of terminal rendering attributes src/rect.c - implements TickitRect; utility structure representing a rectangular region src/rectset.c - implements TickitRectSet; stores a collection of rectangular areas src/renderbuffer.c - implements TickitRenderBuffer; store display content to be drawn to the terminal src/string.c - implements TickitString; a reference-counted string buffer src/term.c - implements TickitTerm; abstraction of an interactive terminal src/termdriver-ti.c - a terminal IO driver which uses terminfo src/termdriver-xterm.c - a terminal IO driver for xterm-like terminals src/termdriver.h - header file used for terminal drivers src/tickit.c - implements Tickit; the toplevel instance and event loop controller src/unicode.h - internal function which handles the terminal width of Unicode codepoints src/utf8.c - Unicode and UTF-8 string handling functions src/window.c - implements TickitWindow; a window for drawing operations and input src/xterm-palette.inc.PL - script to generate `xterm-palette.inc` src/xterm-palette.inc - defines an array to downsample xterm256 colours to 8- or 16-colour palette t/ - contains unit tests tickit.pc.in - template for the file installed as `PREFIX/pkgconfig/tickit.pc` libtickit-0.3.4/CREDITS0000644000000000000000000000011113613430267012640 0ustar 00000000000000Bruce Mitchener (@waywardmonkeys on github): the original src/window.c libtickit-0.3.4/LICENSE0000644000000000000000000000211713613430267012635 0ustar 00000000000000 The MIT License Copyright (c) 2012-2015 Paul Evans Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. libtickit-0.3.4/Makefile0000644000000000000000000001022113613430267013263 0ustar 00000000000000ifeq ($(shell uname),Darwin) LIBTOOL ?= glibtool else LIBTOOL ?= libtool endif ifneq ($(VERBOSE),1) LIBTOOL +=--quiet endif override CFLAGS +=-Wall -Iinclude -Isrc -std=c99 ifeq ($(DEBUG),1) override CFLAGS +=-ggdb -DDEBUG endif ifeq ($(PROFILE),1) override CFLAGS +=-pg override LDFLAGS+=-pg endif ifeq ($(shell pkg-config --atleast-version=1.1.0 unibilium && echo 1),1) override CFLAGS +=$(shell pkg-config --cflags unibilium) -DHAVE_UNIBILIUM override LDFLAGS+=$(shell pkg-config --libs unibilium) else ifeq ($(shell pkg-config ncursesw && echo 1),1) override CFLAGS +=$(shell pkg-config --cflags ncursesw) override LDFLAGS+=$(shell pkg-config --libs ncursesw) else override LDFLAGS+=-lncurses endif override CFLAGS +=$(shell pkg-config --cflags termkey) override LDFLAGS+=$(shell pkg-config --libs termkey) CFILES=$(sort $(wildcard src/*.c)) HFILES=$(sort $(wildcard include/*.h)) OBJECTS=$(CFILES:.c=.lo) LIBRARY=libtickit.la HFILES_INT=$(sort $(wildcard src/*.h)) $(HFILES) TESTSOURCES=$(sort $(wildcard t/[0-9]*.c)) TESTFILES=$(TESTSOURCES:.c=.t) EXAMPLESOURCES=$(sort $(wildcard examples/*.c)) VERSION_CURRENT=2 VERSION_REVISION=4 VERSION_AGE=0 PREFIX=/usr/local BINDIR=$(PREFIX)/bin LIBDIR=$(PREFIX)/lib INCDIR=$(PREFIX)/include MANDIR=$(PREFIX)/share/man MAN3DIR=$(MANDIR)/man3 MAN7DIR=$(MANDIR)/man7 all: $(LIBRARY) $(LIBRARY): $(OBJECTS) $(LIBTOOL) --mode=link --tag=CC $(CC) -rpath $(LIBDIR) -version-info $(VERSION_CURRENT):$(VERSION_REVISION):$(VERSION_AGE) -o $@ $^ $(LDFLAGS) src/%.lo: src/%.c $(HFILES_INT) $(LIBTOOL) --mode=compile --tag=CC $(CC) $(CFLAGS) -o $@ -c $< src/term.lo: src/xterm-palette.inc src/xterm-palette.inc: src/xterm-palette.inc.PL perl $^ > $@ src/renderbuffer.lo: src/linechars.inc src/linechars.inc: src/linechars.inc.PL perl $^ > $@ t/%.t: t/%.lo $(LIBRARY) t/taplib.lo t/mockterm.lo t/taplib-tickit.lo $(LIBTOOL) --mode=link --tag=CC $(CC) -o $@ $^ $(LDFLAGS) t/%.lo: t/%.c $(LIBTOOL) --mode=compile --tag=CC $(CC) $(CFLAGS) -o $@ -c $^ .PHONY: test test: $(TESTFILES) $(LIBTOOL) --mode=execute prove -e "" .PHONY: clean-test clean-test: $(LIBTOOL) --mode=clean rm -f $(TESTFILES) t/taplib.lo t/mockterm.lo .PHONY: clean clean: clean-test $(LIBTOOL) --mode=clean rm -f $(OBJECTS) $(LIBTOOL) --mode=clean rm -f $(LIBRARY) ifneq ($(shell pkg-config glib-2.0 && echo 1),1) EXAMPLESOURCES:=$(filter-out examples/evloop-glib.c, $(EXAMPLESOURCES)) endif examples/evloop-glib.lo: CFLAGS +=$(shell pkg-config --cflags glib-2.0) examples/evloop-glib: LDFLAGS+=$(shell pkg-config --libs glib-2.0) ifneq ($(shell pkg-config libuv && echo 1),1) EXAMPLESOURCES:=$(filter-out examples/evloop-libuv.c, $(EXAMPLESOURCES)) endif examples/evloop-libuv.lo: CFLAGS +=$(shell pkg-config --cflags libuv) examples/evloop-libuv: LDFLAGS+=$(shell pkg-config --libs libuv) .PHONY: examples examples: $(EXAMPLESOURCES:.c=) examples/%.lo: examples/%.c $(HFILES) $(LIBTOOL) --mode=compile --tag=CC $(CC) $(CFLAGS) -o $@ -c $< examples/%: examples/%.lo $(LIBRARY) $(LIBTOOL) --mode=link --tag=CC $(CC) -o $@ $^ $(LDFLAGS) .PHONY: install install: install-inc install-lib install-man $(LIBTOOL) --mode=finish $(DESTDIR)$(LIBDIR) install-inc: $(HFILES) install -d $(DESTDIR)$(INCDIR) install -m644 $(HFILES) $(DESTDIR)$(INCDIR) install -d $(DESTDIR)$(LIBDIR)/pkgconfig LIBDIR=$(LIBDIR) INCDIR=$(INCDIR) sh tickit.pc.sh >$(DESTDIR)$(LIBDIR)/pkgconfig/tickit.pc # rm the old binary first in case it's still in use install-lib: $(LIBRARY) install -d $(DESTDIR)$(LIBDIR) $(LIBTOOL) --mode=install install $(LIBRARY) $(DESTDIR)$(LIBDIR)/ install-man: install -d $(DESTDIR)$(MAN3DIR) install -d $(DESTDIR)$(MAN7DIR) for F in man/*.3; do \ gzip <$$F >$(DESTDIR)$(MAN3DIR)/$${F#man/}.gz; \ done for F in man/*.7; do \ gzip <$$F >$(DESTDIR)$(MAN7DIR)/$${F#man/}.gz; \ done while read FROM EQ TO; do \ if [ -n "$$FROM" ]; then \ ln -sf $$TO.gz $(DESTDIR)$(MAN3DIR)/$$FROM.gz; \ fi; \ done < man/also HTMLDIR=html htmldocs: perl $(HOME)/src/perl/Parse-Man/examples/man-to-html.pl -O $(HTMLDIR) --file-extension html --link-extension html --template home_lou.tt2 --also man/also man/*.3 man/*.7 --index html/index.html libtickit-0.3.4/examples/0000755000000000000000000000000013613430267013445 5ustar 00000000000000libtickit-0.3.4/include/0000755000000000000000000000000013613430267013252 5ustar 00000000000000libtickit-0.3.4/man/0000755000000000000000000000000013613430267012402 5ustar 00000000000000libtickit-0.3.4/src/0000755000000000000000000000000013613430267012416 5ustar 00000000000000libtickit-0.3.4/t/0000755000000000000000000000000013613430267012072 5ustar 00000000000000libtickit-0.3.4/tickit.pc.sh0000644000000000000000000000033313613430267014052 0ustar 00000000000000#!/bin/sh LIBS='-L${libdir} -ltickit' CFLAGS='-I${includedir}' cat < #include #include #include #include #define streq(a,b) (!strcmp(a,b)) struct { char *name; int val; TickitPen *pen_fg, *pen_fg_hi, *pen_bg, *pen_bg_hi; } colours[] = { { "red ", 1 }, { "blue ", 4 }, { "green ", 2 }, { "yellow", 3 }, }; struct { char *name; TickitPenAttr attr; TickitPen *pen; } attrs[] = { { "bold", TICKIT_PEN_BOLD }, { "underline", TICKIT_PEN_UNDER }, { "italic", TICKIT_PEN_ITALIC }, { "strikethrough", TICKIT_PEN_STRIKE }, { "reverse video", TICKIT_PEN_REVERSE }, { "blink", TICKIT_PEN_BLINK }, { "alternate font", TICKIT_PEN_ALTFONT }, }; static int on_expose(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; TickitRenderBuffer *rb = info->rb; tickit_renderbuffer_eraserect(rb, &info->rect); /* ANSI colours foreground */ tickit_renderbuffer_goto(rb, 0, 0); for(int i = 0; i < 4; i++) { tickit_renderbuffer_savepen(rb); if(!colours[i].pen_fg) colours[i].pen_fg = tickit_pen_new_attrs( TICKIT_PEN_FG, colours[i].val, 0); tickit_renderbuffer_setpen(rb, colours[i].pen_fg); tickit_renderbuffer_textf(rb, "fg %s", colours[i].name); tickit_renderbuffer_restore(rb); tickit_renderbuffer_text(rb, " "); } /* ANSI high-brightness colours foreground */ tickit_renderbuffer_goto(rb, 2, 0); for(int i = 0; i < 4; i++) { tickit_renderbuffer_savepen(rb); if(!colours[i].pen_fg_hi) colours[i].pen_fg_hi = tickit_pen_new_attrs( TICKIT_PEN_FG, colours[i].val+8, 0); tickit_renderbuffer_setpen(rb, colours[i].pen_fg_hi); tickit_renderbuffer_textf(rb, "fg hi-%s", colours[i].name); tickit_renderbuffer_restore(rb); tickit_renderbuffer_text(rb, " "); } /* ANSI colours background */ tickit_renderbuffer_goto(rb, 4, 0); for(int i = 0; i < 4; i++) { tickit_renderbuffer_savepen(rb); if(!colours[i].pen_bg) colours[i].pen_bg = tickit_pen_new_attrs( TICKIT_PEN_BG, colours[i].val, TICKIT_PEN_FG, 0, 0); tickit_renderbuffer_setpen(rb, colours[i].pen_bg); tickit_renderbuffer_textf(rb, "bg %s", colours[i].name); tickit_renderbuffer_restore(rb); tickit_renderbuffer_text(rb, " "); } tickit_renderbuffer_goto(rb, 6, 0); /* ANSI high-brightness colours background */ for(int i = 0; i < 4; i++) { tickit_renderbuffer_savepen(rb); if(!colours[i].pen_bg_hi) colours[i].pen_bg_hi = tickit_pen_new_attrs( TICKIT_PEN_BG, colours[i].val+8, TICKIT_PEN_FG, 0, 0); tickit_renderbuffer_setpen(rb, colours[i].pen_bg_hi); tickit_renderbuffer_textf(rb, "bg hi-%s", colours[i].name); tickit_renderbuffer_restore(rb); tickit_renderbuffer_text(rb, " "); } /* Some interesting rendering attributes */ for(int i = 0; i < 7; i++) { tickit_renderbuffer_savepen(rb); tickit_renderbuffer_goto(rb, 8 + 2*i, 0); if(!attrs[i].pen) attrs[i].pen = tickit_pen_new_attrs(attrs[i].attr, 1, 0); tickit_renderbuffer_setpen(rb, attrs[i].pen); tickit_renderbuffer_text(rb, attrs[i].name); if(attrs[i].attr == TICKIT_PEN_UNDER) { /* show off other styles */ TickitPenUnderline under; for(under = TICKIT_PEN_UNDER_DOUBLE; under < TICKIT_N_PEN_UNDERS; under++) { TickitPen *pen = tickit_pen_new_attrs(TICKIT_PEN_UNDER, under, 0); tickit_renderbuffer_skip(rb, 4); tickit_renderbuffer_setpen(rb, pen); tickit_renderbuffer_text(rb, (char *[]){NULL, NULL, "double-underline", "undercurl"}[under]); } } tickit_renderbuffer_restore(rb); } return 1; } static int checksuspend(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitKeyEventInfo *info = _info; if(info->mod != TICKIT_MOD_CTRL || !streq(info->str, "C-z")) return 0; TickitTerm *term = tickit_window_get_term(win); tickit_term_pause(term); raise(SIGSTOP); tickit_term_resume(term); tickit_window_expose(win, NULL); return 1; } int main(int argc, char *argv[]) { Tickit *t = tickit_new_stdio(); TickitWindow *root = tickit_get_rootwin(t); if(!root) { fprintf(stderr, "Cannot create TickitTerm - %s\n", strerror(errno)); return 1; } tickit_window_bind_event(root, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose, NULL); tickit_window_bind_event(root, TICKIT_WINDOW_ON_KEY, 0, &checksuspend, NULL); tickit_run(t); tickit_window_close(root); tickit_unref(t); return 0; } libtickit-0.3.4/examples/demo-rgb8.c0000644000000000000000000000554013613430267015401 0ustar 00000000000000#include "tickit.h" #include #include #include #include #include #define streq(a,b) (!strcmp(a,b)) #define COL_RED 1 #define COL_GREEN 2 #define COL_BLUE 4 static int on_geomchange(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { /* re-expose the entire window if it changes shape */ tickit_window_expose(win, NULL); return 1; } static int on_expose(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; TickitRenderBuffer *rb = info->rb; int cols = tickit_window_cols(win); TickitPen *pen = tickit_pen_new(); tickit_renderbuffer_eraserect(rb, &info->rect); /* Red */ tickit_pen_set_colour_attr(pen, TICKIT_PEN_FG, COL_RED); tickit_renderbuffer_setpen(rb, pen); tickit_renderbuffer_text_at(rb, 1, 0, "Red:"); tickit_renderbuffer_goto(rb, 2, 0); for(int x = 0; x < cols; x++) { tickit_pen_set_colour_attr(pen, TICKIT_PEN_BG, x > cols/2 ? COL_RED : 0); tickit_pen_set_colour_attr_rgb8(pen, TICKIT_PEN_BG, (TickitPenRGB8){ .r = 255 * x / (cols-1), .g = 0, .b = 0 }); tickit_renderbuffer_setpen(rb, pen); tickit_renderbuffer_text(rb, " "); } tickit_pen_clear_attr(pen, TICKIT_PEN_BG); /* Green */ tickit_pen_set_colour_attr(pen, TICKIT_PEN_FG, COL_GREEN); tickit_renderbuffer_setpen(rb, pen); tickit_renderbuffer_text_at(rb, 5, 0, "Green:"); tickit_renderbuffer_goto(rb, 6, 0); for(int x = 0; x < cols; x++) { tickit_pen_set_colour_attr(pen, TICKIT_PEN_BG, x > cols/2 ? COL_GREEN : 0); tickit_pen_set_colour_attr_rgb8(pen, TICKIT_PEN_BG, (TickitPenRGB8){ .r = 0, .g = 255 * x / (cols-1), .b = 0 }); tickit_renderbuffer_setpen(rb, pen); tickit_renderbuffer_text(rb, " "); } tickit_pen_clear_attr(pen, TICKIT_PEN_BG); /* Blue */ tickit_pen_set_colour_attr(pen, TICKIT_PEN_FG, COL_BLUE); tickit_renderbuffer_setpen(rb, pen); tickit_renderbuffer_text_at(rb, 9, 0, "Blue:"); tickit_renderbuffer_goto(rb, 10, 0); for(int x = 0; x < cols; x++) { tickit_pen_set_colour_attr(pen, TICKIT_PEN_BG, x > cols/2 ? COL_BLUE : 0); tickit_pen_set_colour_attr_rgb8(pen, TICKIT_PEN_BG, (TickitPenRGB8){ .r = 0, .g = 0, .b = 255 * x / (cols-1) }); tickit_renderbuffer_setpen(rb, pen); tickit_renderbuffer_text(rb, " "); } tickit_pen_clear_attr(pen, TICKIT_PEN_BG); return 1; } int main(int argc, char *argv[]) { Tickit *t = tickit_new_stdio(); TickitWindow *root = tickit_get_rootwin(t); if(!root) { fprintf(stderr, "Cannot create TickitTerm - %s\n", strerror(errno)); return 1; } tickit_window_bind_event(root, TICKIT_WINDOW_ON_GEOMCHANGE, 0, &on_geomchange, NULL); tickit_window_bind_event(root, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose, NULL); tickit_run(t); tickit_window_close(root); tickit_unref(t); return 0; } libtickit-0.3.4/examples/demo-scroll.c0000644000000000000000000000620613613430267016035 0ustar 00000000000000#include "tickit.h" #include #include #include #include int term_lines, term_cols; static void scroll(TickitTerm *tt, int downward, int rightward) { tickit_term_scrollrect(tt, (TickitRect){ 2, 2, term_lines - 4, term_cols - 4 }, downward, rightward); } static void resize(TickitTerm *tt, int lines, int cols) { term_lines = lines; term_cols = cols; tickit_term_goto(tt, 0, 2); tickit_term_print(tt, "Scroll up"); tickit_term_goto(tt, 1, 2); tickit_term_print(tt, "-----------"); tickit_term_goto(tt, lines - 2, 2); tickit_term_print(tt, "-----------"); tickit_term_goto(tt, lines - 1, 2); tickit_term_print(tt, "Scroll down"); for(int line = 0; line < 12; line++) { char buf[3]; sprintf(buf, "%c-", "Scroll left "[line]); tickit_term_goto(tt, line + 2, 0); tickit_term_print(tt, buf); sprintf(buf, "-%c", "Scroll right"[line]); tickit_term_goto(tt, line + 2, cols - 2); tickit_term_print(tt, buf); } } static int key_event(TickitTerm *tt, TickitEventFlags flags, void *_info, void *data) { TickitKeyEventInfo *info = _info; if(info->type != TICKIT_KEYEV_KEY) return 1; const char *key = info->str; if(strcmp(key, "Up") == 0) scroll(tt, -1, 0); else if(strcmp(key, "Down") == 0) scroll(tt, +1, 0); else if(strcmp(key, "Left") == 0) scroll(tt, 0, -1); else if(strcmp(key, "Right") == 0) scroll(tt, 0, +1); return 1; } static int mouse_event(TickitTerm *tt, TickitEventFlags flags, void *_info, void *data) { TickitMouseEventInfo *info = _info; if(info->button != 1 || info->type != TICKIT_MOUSEEV_PRESS) return 1; int line = info->line; int col = info->col; if(line < 1) { scroll(tt, -1, 0); return 1; } if(line > term_lines - 2) { scroll(tt, +1, 0); return 1; } if(col < 1) { scroll(tt, 0, -1); return 1; } if(col > term_cols - 2) { scroll(tt, 0, +1); return 1; } return 1; } static int resize_event(TickitTerm *tt, TickitEventFlags flags, void *_info, void *data) { TickitResizeEventInfo *info = _info; resize(tt, info->lines, info->cols); return 1; } static int render(Tickit *t, TickitEventFlags flags, void *data, void *user) { TickitTerm *tt = tickit_get_term(t); int lines, cols; tickit_term_get_size(tt, &lines, &cols); resize(tt, lines, cols); for(int line = 2; line < lines - 2; line++) { char buf[64]; sprintf(buf, "content on line %d here", line); tickit_term_goto(tt, line, 5); tickit_term_print(tt, buf); } return 0; } int main(int argc, char *argv[]) { Tickit *t = tickit_new_stdio(); TickitTerm *tt = tickit_get_term(t); if(!tt) { fprintf(stderr, "Cannot create TickitTerm - %s\n", strerror(errno)); return 1; } tickit_term_setctl_int(tt, TICKIT_TERMCTL_MOUSE, TICKIT_TERM_MOUSEMODE_CLICK); tickit_term_bind_event(tt, TICKIT_TERM_ON_KEY, 0, &key_event, NULL); tickit_term_bind_event(tt, TICKIT_TERM_ON_MOUSE, 0, &mouse_event, NULL); tickit_term_bind_event(tt, TICKIT_TERM_ON_RESIZE, 0, &resize_event, NULL); tickit_watch_later(t, 0, &render, NULL); tickit_run(t); tickit_unref(t); return 0; } libtickit-0.3.4/examples/demo-termctl.c0000644000000000000000000000607513613430267016215 0ustar 00000000000000#include "tickit.h" #include #include #include #include static struct { int vis : 1; int blink : 1; unsigned int shape : 2; } modes; static void render_modes(TickitTerm *tt) { tickit_term_goto(tt, 5, 3); tickit_term_print(tt, "Cursor visible: "); tickit_term_print(tt, modes.vis ? "| >On< | Off |" : "| On | >Off< |"); tickit_term_goto(tt, 7, 3); tickit_term_print(tt, "Cursor blink: "); tickit_term_print(tt, modes.blink ? "| >On< | Off |" : "| On | >Off< |"); tickit_term_goto(tt, 9, 3); tickit_term_print(tt, "Cursor shape: "); tickit_term_print(tt, modes.shape == TICKIT_CURSORSHAPE_BLOCK ? "| >Block< | Under | Bar |" : modes.shape == TICKIT_CURSORSHAPE_UNDER ? "| Block | >Under< | Bar |" : "| Block | Under | >Bar< |"); tickit_term_goto(tt, 20, 10); tickit_term_print(tt, "Cursor > <"); tickit_term_goto(tt, 20, 20); } static int event(TickitTerm *tt, TickitEventFlags flags, void *_info, void *data) { TickitMouseEventInfo *info = _info; if(info->type != TICKIT_MOUSEEV_PRESS || info->button != 1) return 0; if(info->line == 5) { if(info->col >= 21 && info->col <= 26) modes.vis = 1; else if(info->col >= 28 && info->col <= 34) modes.vis = 0; else return 0; tickit_term_setctl_int(tt, TICKIT_TERMCTL_CURSORVIS, modes.vis); } if(info->line == 7) { if(info->col >= 21 && info->col <= 26) modes.blink = 1; else if(info->col >= 28 && info->col <= 34) modes.blink = 0; else return 0; tickit_term_setctl_int(tt, TICKIT_TERMCTL_CURSORBLINK, modes.blink); } if(info->line == 9) { if(info->col >= 21 && info->col <= 29) modes.shape = TICKIT_CURSORSHAPE_BLOCK; else if(info->col >= 31 && info->col <= 39) modes.shape = TICKIT_CURSORSHAPE_UNDER; else if(info->col >= 40 && info->col <= 47) modes.shape = TICKIT_CURSORSHAPE_LEFT_BAR; else return 0; tickit_term_setctl_int(tt, TICKIT_TERMCTL_CURSORSHAPE, modes.shape); } render_modes(tt); return 1; } static int later(Tickit *t, TickitEventFlags flags, void *_info, void *data) { TickitTerm *tt = tickit_get_term(t); render_modes(tt); tickit_term_setctl_int(tt, TICKIT_TERMCTL_CURSORVIS, modes.vis); return 1; } int main(int argc, char *argv[]) { Tickit *t = tickit_new_stdio(); TickitTerm *tt = tickit_get_term(t); if(!tt) { fprintf(stderr, "Cannot create TickitTerm - %s\n", strerror(errno)); return 1; } tickit_term_setctl_int(tt, TICKIT_TERMCTL_MOUSE, TICKIT_TERM_MOUSEMODE_CLICK); tickit_term_bind_event(tt, TICKIT_TERM_ON_MOUSE, 0, event, NULL); tickit_term_setctl_int(tt, TICKIT_TERMCTL_CURSORVIS, (modes.vis = 1)); tickit_term_setctl_int(tt, TICKIT_TERMCTL_CURSORBLINK, (modes.blink = 1)); tickit_term_setctl_int(tt, TICKIT_TERMCTL_CURSORSHAPE, (modes.shape = TICKIT_CURSORSHAPE_BLOCK)); tickit_watch_later(t, 0, &later, NULL); tickit_run(t); tickit_unref(t); return 0; } libtickit-0.3.4/examples/demo-xterm256.c0000644000000000000000000000425313613430267016133 0ustar 00000000000000#include "tickit.h" #include #include #include #include static int on_expose(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; TickitRenderBuffer *rb = info->rb; tickit_renderbuffer_eraserect(rb, &info->rect); TickitPen *pen = tickit_pen_new(); tickit_renderbuffer_goto(rb, 0, 0); tickit_renderbuffer_text(rb, "ANSI"); { tickit_renderbuffer_save(rb); tickit_renderbuffer_goto(rb, 2, 0); for(int i = 0; i < 16; i++) { tickit_pen_set_colour_attr(pen, TICKIT_PEN_BG, i); tickit_renderbuffer_setpen(rb, pen); tickit_renderbuffer_textf(rb, "[%02d]", i); } tickit_renderbuffer_restore(rb); } tickit_renderbuffer_goto(rb, 4, 0); tickit_renderbuffer_text(rb, "216 RGB cube"); { tickit_renderbuffer_save(rb); for(int y = 0; y < 6; y++) { tickit_renderbuffer_goto(rb, 6+y, 0); for(int x = 0; x < 36; x++) { tickit_pen_set_colour_attr(pen, TICKIT_PEN_BG, y*36 + x + 16); tickit_renderbuffer_setpen(rb, pen); tickit_renderbuffer_text(rb, " "); } } tickit_renderbuffer_restore(rb); } tickit_renderbuffer_goto(rb, 13, 0); tickit_renderbuffer_text(rb, "24 Greyscale ramp"); { tickit_renderbuffer_save(rb); tickit_renderbuffer_goto(rb, 15, 0); for(int i = 0; i < 24; i++) { tickit_pen_set_colour_attr(pen, TICKIT_PEN_BG, 232 + i); if(i > 12) tickit_pen_set_colour_attr(pen, TICKIT_PEN_FG, 0); tickit_renderbuffer_setpen(rb, pen); tickit_renderbuffer_textf(rb, "g%02d", i); } tickit_renderbuffer_restore(rb); } tickit_pen_unref(pen); return 1; } int main(int argc, char *argv[]) { Tickit *t = tickit_new_stdio(); TickitWindow *root = tickit_get_rootwin(t); if(!root) { fprintf(stderr, "Cannot create TickitTerm - %s\n", strerror(errno)); return 1; } tickit_term_setctl_str(tickit_get_term(t), TICKIT_TERMCTL_TITLE_TEXT, "XTerm256 colour demo"); tickit_window_bind_event(root, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose, NULL); tickit_run(t); tickit_window_close(root); tickit_unref(t); return 0; } libtickit-0.3.4/examples/demo.c0000644000000000000000000001747713613430267014555 0ustar 00000000000000#ifdef __GLIBC__ # define _XOPEN_SOURCE 500 /* strdup */ #endif #include "tickit.h" #include #include #include #include TickitKeyEventInfo lastkey; TickitWindow *keywin; TickitMouseEventInfo lastmouse; TickitWindow *mousewin; int counter = 0; TickitWindow *timerwin; static TickitPen *mkpen_highlight(void) { static TickitPen *pen; if(!pen) pen = tickit_pen_new_attrs( TICKIT_PEN_FG, 3, TICKIT_PEN_BOLD, 1, 0); return pen; } static void render_modifier(TickitRenderBuffer *rb, int mod) { if(!mod) return; int pipe = 0; tickit_renderbuffer_text(rb, "<"); if(mod & TICKIT_MOD_SHIFT) tickit_renderbuffer_text(rb, pipe++ ? "|SHIFT" : "SHIFT"); if(mod & TICKIT_MOD_ALT) tickit_renderbuffer_text(rb, pipe++ ? "|ALT" : "ALT"); if(mod & TICKIT_MOD_CTRL) tickit_renderbuffer_text(rb, pipe++ ? "|CTRL" : "CTRL"); tickit_renderbuffer_text(rb, ">"); } static int render_key(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; TickitRenderBuffer *rb = info->rb; tickit_renderbuffer_eraserect(rb, &info->rect); tickit_renderbuffer_goto(rb, 0, 0); { tickit_renderbuffer_savepen(rb); tickit_renderbuffer_setpen(rb, mkpen_highlight()); tickit_renderbuffer_text(rb, "Key:"); tickit_renderbuffer_restore(rb); } tickit_renderbuffer_goto(rb, 2, 2); switch(lastkey.type) { case TICKIT_KEYEV_TEXT: tickit_renderbuffer_text(rb, "text "); break; case TICKIT_KEYEV_KEY: tickit_renderbuffer_text(rb, "key "); break; default: return 0; } tickit_renderbuffer_text(rb, lastkey.str); render_modifier(rb, lastkey.mod); return 1; } static int event_key(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitKeyEventInfo *info = _info; if(lastkey.str) free((void *)lastkey.str); lastkey = *info; lastkey.str = strdup(info->str); tickit_window_expose(keywin, NULL); return 1; } static int render_mouse(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; TickitRenderBuffer *rb = info->rb; tickit_renderbuffer_eraserect(rb, &info->rect); tickit_renderbuffer_goto(rb, 0, 0); { tickit_renderbuffer_savepen(rb); tickit_renderbuffer_setpen(rb, mkpen_highlight()); tickit_renderbuffer_text(rb, "Mouse:"); tickit_renderbuffer_restore(rb); } tickit_renderbuffer_goto(rb, 2, 2); switch(lastmouse.type) { case TICKIT_MOUSEEV_PRESS: tickit_renderbuffer_text(rb, "press "); break; case TICKIT_MOUSEEV_DRAG: tickit_renderbuffer_text(rb, "drag "); break; case TICKIT_MOUSEEV_RELEASE: tickit_renderbuffer_text(rb, "release "); break; case TICKIT_MOUSEEV_WHEEL: tickit_renderbuffer_text(rb, "wheel "); break; default: return 0; } if(lastmouse.type == TICKIT_MOUSEEV_WHEEL) { tickit_renderbuffer_text(rb, lastmouse.button == TICKIT_MOUSEWHEEL_DOWN ? "down" : "up"); } else { tickit_renderbuffer_textf(rb, "button %d", lastmouse.button); } tickit_renderbuffer_textf(rb, " at (%d,%d)", lastmouse.line, lastmouse.col); render_modifier(rb, lastmouse.mod); return 1; } static int event_mouse(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitMouseEventInfo *info = _info; lastmouse = *info; tickit_window_expose(mousewin, NULL); return 1; } static int render_timer(TickitWindow *win, TickitEventFlags flags, void *_info, void *user) { int *counterp = user; TickitExposeEventInfo *info = _info; TickitRenderBuffer *rb = info->rb; tickit_renderbuffer_eraserect(rb, &info->rect); tickit_renderbuffer_goto(rb, 0, 0); { tickit_renderbuffer_savepen(rb); tickit_renderbuffer_setpen(rb, mkpen_highlight()); tickit_renderbuffer_text(rb, "Counter:"); tickit_renderbuffer_restore(rb); } tickit_renderbuffer_goto(rb, 2, 2); tickit_renderbuffer_textf(rb, "%d", *counterp); return 1; } static int on_timer(Tickit *t, TickitEventFlags flags, void *_info, void *user); static int on_timer(Tickit *t, TickitEventFlags flags, void *_info, void *user) { int *counterp = user; (*counterp)++; tickit_window_expose(timerwin, NULL); tickit_watch_timer_after_msec(t, 1000, 0, &on_timer, user); return 0; } static int render_root(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; TickitRenderBuffer *rb = info->rb; int right = tickit_window_cols(win) - 1; int bottom = tickit_window_lines(win) - 1; tickit_renderbuffer_eraserect(rb, &(TickitRect){ .top = 0, .left = 0, .lines = bottom+1, .cols = right+1, }); static TickitPen *pen_blue; if(!pen_blue) pen_blue = tickit_pen_new_attrs( TICKIT_PEN_FG, 4+8, 0); static TickitPen *pen_white; if(!pen_white) pen_white = tickit_pen_new_attrs( TICKIT_PEN_FG, 7+8, 0); // Draw a horizontal size marker bar { tickit_renderbuffer_setpen(rb, pen_blue); tickit_renderbuffer_hline_at(rb, 1, 0, right, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_vline_at(rb, 0, 2, 0, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_vline_at(rb, 0, 2, right, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_setpen(rb, pen_white); tickit_renderbuffer_goto(rb, 1, (right / 2) - 2); tickit_renderbuffer_textf(rb, " %d ", right + 1); // cols } int left = right - 4; // Draw a vertical size marker bar { tickit_renderbuffer_setpen(rb, pen_blue); tickit_renderbuffer_vline_at(rb, 0, bottom, left + 2, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_hline_at(rb, 0, left, right, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_hline_at(rb, bottom, left, right, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_setpen(rb, pen_white); tickit_renderbuffer_goto(rb, (bottom / 2) - 1, left); tickit_renderbuffer_textf(rb, "%d", bottom + 1); // lines } return 1; } static int event_resize(TickitWindow *root, TickitEventFlags flags, void *_info, void *data) { int cols = tickit_window_cols(root); tickit_window_set_geometry(keywin, (TickitRect){ .top = 2, .left = 2, .lines = 3, .cols = cols - 7 }); tickit_window_set_geometry(mousewin, (TickitRect){ .top = 8, .left = 2, .lines = 3, .cols = cols - 7 }); tickit_window_set_geometry(timerwin, (TickitRect){ .top = 15, .left = 2, .lines = 3, .cols = cols - 7 }); tickit_window_expose(root, NULL); return 1; } int main(int argc, char *argv[]) { Tickit *t = tickit_new_stdio(); TickitWindow *root = tickit_get_rootwin(t); if(!root) { fprintf(stderr, "Cannot create TickitTerm - %s\n", strerror(errno)); return 1; } keywin = tickit_window_new(root, (TickitRect){ .top = 2, .left = 2, .lines = 3, .cols = tickit_window_cols(root) - 7 }, 0); tickit_window_bind_event(keywin, TICKIT_WINDOW_ON_EXPOSE, 0, &render_key, NULL); tickit_window_bind_event(root, TICKIT_WINDOW_ON_KEY, 0, &event_key, NULL); mousewin = tickit_window_new(root, (TickitRect){ .top = 8, .left = 2, .lines = 3, .cols = tickit_window_cols(root) - 7 }, 0); tickit_window_bind_event(mousewin, TICKIT_WINDOW_ON_EXPOSE, 0, &render_mouse, NULL); tickit_window_bind_event(root, TICKIT_WINDOW_ON_MOUSE, 0, &event_mouse, NULL); timerwin = tickit_window_new(root, (TickitRect){ .top = 15, .left = 2, .lines = 3, .cols = tickit_window_cols(root) - 7 }, 0); tickit_window_bind_event(timerwin, TICKIT_WINDOW_ON_EXPOSE, 0, &render_timer, &counter); tickit_window_bind_event(root, TICKIT_WINDOW_ON_EXPOSE, 0, &render_root, NULL); tickit_window_bind_event(root, TICKIT_WINDOW_ON_GEOMCHANGE, 0, &event_resize, NULL); tickit_watch_timer_after_msec(t, 1000, 0, &on_timer, &counter); tickit_window_take_focus(root); tickit_window_set_cursor_visible(root, false); tickit_run(t); tickit_window_close(root); tickit_unref(t); return 0; } libtickit-0.3.4/examples/evloop-glib.c0000644000000000000000000002541713613430267016041 0ustar 00000000000000#ifdef __GLIBC__ # define _XOPEN_SOURCE 500 /* strdup */ #endif #include "tickit.h" #include "tickit-evloop.h" #include #include #include #include #include #include #include TickitKeyEventInfo lastkey; TickitWindow *keywin; TickitMouseEventInfo lastmouse; TickitWindow *mousewin; int counter = 0; TickitWindow *timerwin; static TickitPen *mkpen_highlight(void) { static TickitPen *pen; if(!pen) pen = tickit_pen_new_attrs( TICKIT_PEN_FG, 3, TICKIT_PEN_BOLD, 1, 0); return pen; } static void render_modifier(TickitRenderBuffer *rb, int mod) { if(!mod) return; int pipe = 0; tickit_renderbuffer_text(rb, "<"); if(mod & TICKIT_MOD_SHIFT) tickit_renderbuffer_text(rb, pipe++ ? "|SHIFT" : "SHIFT"); if(mod & TICKIT_MOD_ALT) tickit_renderbuffer_text(rb, pipe++ ? "|ALT" : "ALT"); if(mod & TICKIT_MOD_CTRL) tickit_renderbuffer_text(rb, pipe++ ? "|CTRL" : "CTRL"); tickit_renderbuffer_text(rb, ">"); } static int render_key(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; TickitRenderBuffer *rb = info->rb; tickit_renderbuffer_eraserect(rb, &info->rect); tickit_renderbuffer_goto(rb, 0, 0); { tickit_renderbuffer_savepen(rb); tickit_renderbuffer_setpen(rb, mkpen_highlight()); tickit_renderbuffer_text(rb, "Key:"); tickit_renderbuffer_restore(rb); } tickit_renderbuffer_goto(rb, 2, 2); switch(lastkey.type) { case TICKIT_KEYEV_TEXT: tickit_renderbuffer_text(rb, "text "); break; case TICKIT_KEYEV_KEY: tickit_renderbuffer_text(rb, "key "); break; default: return 0; } tickit_renderbuffer_text(rb, lastkey.str); render_modifier(rb, lastkey.mod); return 1; } static int event_key(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitKeyEventInfo *info = _info; if(lastkey.str) free((void *)lastkey.str); lastkey = *info; lastkey.str = strdup(info->str); tickit_window_expose(keywin, NULL); return 1; } static int render_mouse(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; TickitRenderBuffer *rb = info->rb; tickit_renderbuffer_eraserect(rb, &info->rect); tickit_renderbuffer_goto(rb, 0, 0); { tickit_renderbuffer_savepen(rb); tickit_renderbuffer_setpen(rb, mkpen_highlight()); tickit_renderbuffer_text(rb, "Mouse:"); tickit_renderbuffer_restore(rb); } tickit_renderbuffer_goto(rb, 2, 2); switch(lastmouse.type) { case TICKIT_MOUSEEV_PRESS: tickit_renderbuffer_text(rb, "press "); break; case TICKIT_MOUSEEV_DRAG: tickit_renderbuffer_text(rb, "drag "); break; case TICKIT_MOUSEEV_RELEASE: tickit_renderbuffer_text(rb, "release "); break; case TICKIT_MOUSEEV_WHEEL: tickit_renderbuffer_text(rb, "wheel "); break; default: return 0; } if(lastmouse.type == TICKIT_MOUSEEV_WHEEL) { tickit_renderbuffer_text(rb, lastmouse.button == TICKIT_MOUSEWHEEL_DOWN ? "down" : "up"); } else { tickit_renderbuffer_textf(rb, "button %d", lastmouse.button); } tickit_renderbuffer_textf(rb, " at (%d,%d)", lastmouse.line, lastmouse.col); render_modifier(rb, lastmouse.mod); return 1; } static int event_mouse(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitMouseEventInfo *info = _info; lastmouse = *info; tickit_window_expose(mousewin, NULL); return 1; } static int render_timer(TickitWindow *win, TickitEventFlags flags, void *_info, void *user) { int *counterp = user; TickitExposeEventInfo *info = _info; TickitRenderBuffer *rb = info->rb; tickit_renderbuffer_eraserect(rb, &info->rect); tickit_renderbuffer_goto(rb, 0, 0); { tickit_renderbuffer_savepen(rb); tickit_renderbuffer_setpen(rb, mkpen_highlight()); tickit_renderbuffer_text(rb, "Counter:"); tickit_renderbuffer_restore(rb); } tickit_renderbuffer_goto(rb, 2, 2); tickit_renderbuffer_textf(rb, "%d", *counterp); return 1; } static int on_timer(Tickit *t, TickitEventFlags flags, void *_info, void *user); static int on_timer(Tickit *t, TickitEventFlags flags, void *_info, void *user) { int *counterp = user; (*counterp)++; tickit_window_expose(timerwin, NULL); tickit_watch_timer_after_msec(t, 1000, 0, &on_timer, user); return 0; } static int render_root(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; TickitRenderBuffer *rb = info->rb; int right = tickit_window_cols(win) - 1; int bottom = tickit_window_lines(win) - 1; tickit_renderbuffer_eraserect(rb, &(TickitRect){ .top = 0, .left = 0, .lines = bottom+1, .cols = right+1, }); static TickitPen *pen_blue; if(!pen_blue) pen_blue = tickit_pen_new_attrs( TICKIT_PEN_FG, 4+8, 0); static TickitPen *pen_white; if(!pen_white) pen_white = tickit_pen_new_attrs( TICKIT_PEN_FG, 7+8, 0); // Draw a horizontal size marker bar { tickit_renderbuffer_setpen(rb, pen_blue); tickit_renderbuffer_hline_at(rb, 1, 0, right, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_vline_at(rb, 0, 2, 0, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_vline_at(rb, 0, 2, right, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_setpen(rb, pen_white); tickit_renderbuffer_goto(rb, 1, (right / 2) - 2); tickit_renderbuffer_textf(rb, " %d ", right + 1); // cols } int left = right - 4; // Draw a vertical size marker bar { tickit_renderbuffer_setpen(rb, pen_blue); tickit_renderbuffer_vline_at(rb, 0, bottom, left + 2, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_hline_at(rb, 0, left, right, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_hline_at(rb, bottom, left, right, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_setpen(rb, pen_white); tickit_renderbuffer_goto(rb, (bottom / 2) - 1, left); tickit_renderbuffer_textf(rb, "%d", bottom + 1); // lines } return 1; } static int event_resize(TickitWindow *root, TickitEventFlags flags, void *_info, void *data) { int cols = tickit_window_cols(root); tickit_window_set_geometry(keywin, (TickitRect){ .top = 2, .left = 2, .lines = 3, .cols = cols - 7 }); tickit_window_set_geometry(mousewin, (TickitRect){ .top = 8, .left = 2, .lines = 3, .cols = cols - 7 }); tickit_window_set_geometry(timerwin, (TickitRect){ .top = 15, .left = 2, .lines = 3, .cols = cols - 7 }); tickit_window_expose(root, NULL); return 1; } TickitEventHooks glib_evhooks; int main(int argc, char *argv[]) { Tickit *t = tickit_new_with_evloop(NULL, &glib_evhooks, NULL); TickitWindow *root = tickit_get_rootwin(t); if(!root) { fprintf(stderr, "Cannot create TickitTerm - %s\n", strerror(errno)); return 1; } keywin = tickit_window_new(root, (TickitRect){ .top = 2, .left = 2, .lines = 3, .cols = tickit_window_cols(root) - 7 }, 0); tickit_window_bind_event(keywin, TICKIT_WINDOW_ON_EXPOSE, 0, &render_key, NULL); tickit_window_bind_event(root, TICKIT_WINDOW_ON_KEY, 0, &event_key, NULL); mousewin = tickit_window_new(root, (TickitRect){ .top = 8, .left = 2, .lines = 3, .cols = tickit_window_cols(root) - 7 }, 0); tickit_window_bind_event(mousewin, TICKIT_WINDOW_ON_EXPOSE, 0, &render_mouse, NULL); tickit_window_bind_event(root, TICKIT_WINDOW_ON_MOUSE, 0, &event_mouse, NULL); timerwin = tickit_window_new(root, (TickitRect){ .top = 15, .left = 2, .lines = 3, .cols = tickit_window_cols(root) - 7 }, 0); tickit_window_bind_event(timerwin, TICKIT_WINDOW_ON_EXPOSE, 0, &render_timer, &counter); tickit_window_bind_event(root, TICKIT_WINDOW_ON_EXPOSE, 0, &render_root, NULL); tickit_window_bind_event(root, TICKIT_WINDOW_ON_GEOMCHANGE, 0, &event_resize, NULL); tickit_watch_timer_after_msec(t, 1000, 0, &on_timer, &counter); tickit_window_take_focus(root); tickit_window_set_cursor_visible(root, false); tickit_run(t); tickit_window_close(root); tickit_unref(t); return 0; } /* * GLib event loop integration */ typedef struct { GMainLoop *loop; } EventLoopData; static gboolean el_sigwinch(gpointer t) { tickit_evloop_sigwinch(t); return TRUE; } static void *el_init(Tickit *t, void *initdata) { EventLoopData *evdata = malloc(sizeof(EventLoopData)); if(!evdata) return NULL; if(initdata) evdata->loop = initdata; else evdata->loop = g_main_loop_new(NULL, FALSE); g_unix_signal_add(SIGWINCH, el_sigwinch, t); return evdata; } static void el_destroy(void *data) { EventLoopData *evdata = data; if(evdata->loop) g_main_loop_unref(evdata->loop); free(evdata); } static void el_run(void *data, TickitRunFlags flags) { EventLoopData *evdata = data; if(flags & (TICKIT_RUN_ONCE|TICKIT_RUN_NOHANG)) { GMainContext *context = g_main_loop_get_context(evdata->loop); g_main_context_iteration(context, !(flags & TICKIT_RUN_NOHANG)); } else g_main_loop_run(evdata->loop); } static void el_stop(void *data) { EventLoopData *evdata = data; g_main_loop_quit(evdata->loop); } static gboolean fire_io_event(gint fd, GIOCondition condition, gpointer watch) { if(condition & G_IO_IN) { tickit_evloop_invoke_watch(watch, TICKIT_EV_FIRE); } return TRUE; } static bool el_io_read(void *data, int fd, TickitBindFlags flags, TickitWatch *watch) { int id = g_unix_fd_add(fd, G_IO_IN, fire_io_event, watch); tickit_evloop_set_watch_data_int(watch, id); return true; } static void el_cancel_io(void *data, TickitWatch *watch) { g_source_remove(tickit_evloop_get_watch_data_int(watch)); } static gboolean fire_event(gpointer watch) { tickit_evloop_invoke_watch(watch, TICKIT_EV_FIRE); return FALSE; // All non-IO events are oneshot } static bool el_timer(void *data, const struct timeval *at, TickitBindFlags flags, TickitWatch *watch) { struct timeval now; gettimeofday(&now, NULL); long msec = (at->tv_sec - now.tv_sec) * 1000 + (at->tv_usec - now.tv_usec) / 1000; int id = g_timeout_add(msec, fire_event, watch); tickit_evloop_set_watch_data_int(watch, id); return true; } static void el_cancel_timer(void *data, TickitWatch *watch) { g_source_remove(tickit_evloop_get_watch_data_int(watch)); } static bool el_later(void *data, TickitBindFlags flags, TickitWatch *watch) { int id = g_idle_add(fire_event, watch); tickit_evloop_set_watch_data_int(watch, id); return true; } static void el_cancel_later(void *data, TickitWatch *watch) { g_source_remove(tickit_evloop_get_watch_data_int(watch)); } TickitEventHooks glib_evhooks = { .init = el_init, .destroy = el_destroy, .run = el_run, .stop = el_stop, .io_read = el_io_read, .cancel_io = el_cancel_io, .timer = el_timer, .cancel_timer = el_cancel_timer, .later = el_later, .cancel_later = el_cancel_later, }; libtickit-0.3.4/examples/evloop-libuv.c0000644000000000000000000003040313613430267016234 0ustar 00000000000000#ifdef __GLIBC__ # define _XOPEN_SOURCE 500 /* strdup */ #endif #include "tickit.h" #include "tickit-evloop.h" #include #include #include #include #include #include TickitKeyEventInfo lastkey; TickitWindow *keywin; TickitMouseEventInfo lastmouse; TickitWindow *mousewin; int counter = 0; TickitWindow *timerwin; static TickitPen *mkpen_highlight(void) { static TickitPen *pen; if(!pen) pen = tickit_pen_new_attrs( TICKIT_PEN_FG, 3, TICKIT_PEN_BOLD, 1, 0); return pen; } static void render_modifier(TickitRenderBuffer *rb, int mod) { if(!mod) return; int pipe = 0; tickit_renderbuffer_text(rb, "<"); if(mod & TICKIT_MOD_SHIFT) tickit_renderbuffer_text(rb, pipe++ ? "|SHIFT" : "SHIFT"); if(mod & TICKIT_MOD_ALT) tickit_renderbuffer_text(rb, pipe++ ? "|ALT" : "ALT"); if(mod & TICKIT_MOD_CTRL) tickit_renderbuffer_text(rb, pipe++ ? "|CTRL" : "CTRL"); tickit_renderbuffer_text(rb, ">"); } static int render_key(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; TickitRenderBuffer *rb = info->rb; tickit_renderbuffer_eraserect(rb, &info->rect); tickit_renderbuffer_goto(rb, 0, 0); { tickit_renderbuffer_savepen(rb); tickit_renderbuffer_setpen(rb, mkpen_highlight()); tickit_renderbuffer_text(rb, "Key:"); tickit_renderbuffer_restore(rb); } tickit_renderbuffer_goto(rb, 2, 2); switch(lastkey.type) { case TICKIT_KEYEV_TEXT: tickit_renderbuffer_text(rb, "text "); break; case TICKIT_KEYEV_KEY: tickit_renderbuffer_text(rb, "key "); break; default: return 0; } tickit_renderbuffer_text(rb, lastkey.str); render_modifier(rb, lastkey.mod); return 1; } static int event_key(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitKeyEventInfo *info = _info; if(lastkey.str) free((void *)lastkey.str); lastkey = *info; lastkey.str = strdup(info->str); tickit_window_expose(keywin, NULL); return 1; } static int render_mouse(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; TickitRenderBuffer *rb = info->rb; tickit_renderbuffer_eraserect(rb, &info->rect); tickit_renderbuffer_goto(rb, 0, 0); { tickit_renderbuffer_savepen(rb); tickit_renderbuffer_setpen(rb, mkpen_highlight()); tickit_renderbuffer_text(rb, "Mouse:"); tickit_renderbuffer_restore(rb); } tickit_renderbuffer_goto(rb, 2, 2); switch(lastmouse.type) { case TICKIT_MOUSEEV_PRESS: tickit_renderbuffer_text(rb, "press "); break; case TICKIT_MOUSEEV_DRAG: tickit_renderbuffer_text(rb, "drag "); break; case TICKIT_MOUSEEV_RELEASE: tickit_renderbuffer_text(rb, "release "); break; case TICKIT_MOUSEEV_WHEEL: tickit_renderbuffer_text(rb, "wheel "); break; default: return 0; } if(lastmouse.type == TICKIT_MOUSEEV_WHEEL) { tickit_renderbuffer_text(rb, lastmouse.button == TICKIT_MOUSEWHEEL_DOWN ? "down" : "up"); } else { tickit_renderbuffer_textf(rb, "button %d", lastmouse.button); } tickit_renderbuffer_textf(rb, " at (%d,%d)", lastmouse.line, lastmouse.col); render_modifier(rb, lastmouse.mod); return 1; } static int event_mouse(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitMouseEventInfo *info = _info; lastmouse = *info; tickit_window_expose(mousewin, NULL); return 1; } static int render_timer(TickitWindow *win, TickitEventFlags flags, void *_info, void *user) { int *counterp = user; TickitExposeEventInfo *info = _info; TickitRenderBuffer *rb = info->rb; tickit_renderbuffer_eraserect(rb, &info->rect); tickit_renderbuffer_goto(rb, 0, 0); { tickit_renderbuffer_savepen(rb); tickit_renderbuffer_setpen(rb, mkpen_highlight()); tickit_renderbuffer_text(rb, "Counter:"); tickit_renderbuffer_restore(rb); } tickit_renderbuffer_goto(rb, 2, 2); tickit_renderbuffer_textf(rb, "%d", *counterp); return 1; } static int on_timer(Tickit *t, TickitEventFlags flags, void *_info, void *user); static int on_timer(Tickit *t, TickitEventFlags flags, void *_info, void *user) { int *counterp = user; (*counterp)++; tickit_window_expose(timerwin, NULL); tickit_watch_timer_after_msec(t, 1000, 0, &on_timer, user); return 0; } static int render_root(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; TickitRenderBuffer *rb = info->rb; int right = tickit_window_cols(win) - 1; int bottom = tickit_window_lines(win) - 1; tickit_renderbuffer_eraserect(rb, &(TickitRect){ .top = 0, .left = 0, .lines = bottom+1, .cols = right+1, }); static TickitPen *pen_blue; if(!pen_blue) pen_blue = tickit_pen_new_attrs( TICKIT_PEN_FG, 4+8, 0); static TickitPen *pen_white; if(!pen_white) pen_white = tickit_pen_new_attrs( TICKIT_PEN_FG, 7+8, 0); // Draw a horizontal size marker bar { tickit_renderbuffer_setpen(rb, pen_blue); tickit_renderbuffer_hline_at(rb, 1, 0, right, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_vline_at(rb, 0, 2, 0, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_vline_at(rb, 0, 2, right, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_setpen(rb, pen_white); tickit_renderbuffer_goto(rb, 1, (right / 2) - 2); tickit_renderbuffer_textf(rb, " %d ", right + 1); // cols } int left = right - 4; // Draw a vertical size marker bar { tickit_renderbuffer_setpen(rb, pen_blue); tickit_renderbuffer_vline_at(rb, 0, bottom, left + 2, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_hline_at(rb, 0, left, right, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_hline_at(rb, bottom, left, right, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_setpen(rb, pen_white); tickit_renderbuffer_goto(rb, (bottom / 2) - 1, left); tickit_renderbuffer_textf(rb, "%d", bottom + 1); // lines } return 1; } static int event_resize(TickitWindow *root, TickitEventFlags flags, void *_info, void *data) { int cols = tickit_window_cols(root); tickit_window_set_geometry(keywin, (TickitRect){ .top = 2, .left = 2, .lines = 3, .cols = cols - 7 }); tickit_window_set_geometry(mousewin, (TickitRect){ .top = 8, .left = 2, .lines = 3, .cols = cols - 7 }); tickit_window_set_geometry(timerwin, (TickitRect){ .top = 15, .left = 2, .lines = 3, .cols = cols - 7 }); tickit_window_expose(root, NULL); return 1; } TickitEventHooks libuv_evhooks; int main(int argc, char *argv[]) { Tickit *t = tickit_new_with_evloop(NULL, &libuv_evhooks, NULL); TickitWindow *root = tickit_get_rootwin(t); if(!root) { fprintf(stderr, "Cannot create TickitTerm - %s\n", strerror(errno)); return 1; } keywin = tickit_window_new(root, (TickitRect){ .top = 2, .left = 2, .lines = 3, .cols = tickit_window_cols(root) - 7 }, 0); tickit_window_bind_event(keywin, TICKIT_WINDOW_ON_EXPOSE, 0, &render_key, NULL); tickit_window_bind_event(root, TICKIT_WINDOW_ON_KEY, 0, &event_key, NULL); mousewin = tickit_window_new(root, (TickitRect){ .top = 8, .left = 2, .lines = 3, .cols = tickit_window_cols(root) - 7 }, 0); tickit_window_bind_event(mousewin, TICKIT_WINDOW_ON_EXPOSE, 0, &render_mouse, NULL); tickit_window_bind_event(root, TICKIT_WINDOW_ON_MOUSE, 0, &event_mouse, NULL); timerwin = tickit_window_new(root, (TickitRect){ .top = 15, .left = 2, .lines = 3, .cols = tickit_window_cols(root) - 7 }, 0); tickit_window_bind_event(timerwin, TICKIT_WINDOW_ON_EXPOSE, 0, &render_timer, &counter); tickit_window_bind_event(root, TICKIT_WINDOW_ON_EXPOSE, 0, &render_root, NULL); tickit_window_bind_event(root, TICKIT_WINDOW_ON_GEOMCHANGE, 0, &event_resize, NULL); tickit_watch_timer_after_msec(t, 1000, 0, &on_timer, &counter); tickit_window_take_focus(root); tickit_window_set_cursor_visible(root, false); tickit_run(t); tickit_window_close(root); tickit_unref(t); return 0; } /* * UV event loop integration */ static void destroy_handle(uv_handle_t *handle) { free(handle); } typedef struct { uv_loop_t *loop; uv_async_t *stop; uv_signal_t *sigwinch; } EventLoopData; static void el_sigwinch(uv_signal_t *handle, int signum) { Tickit *t = handle->data; tickit_evloop_sigwinch(t); } static void *el_init(Tickit *t, void *initdata) { EventLoopData *evdata = malloc(sizeof(EventLoopData)); if(!evdata) return NULL; if(initdata) evdata->loop = initdata; else evdata->loop = uv_default_loop(); if(!evdata->loop) goto abort; // No callback; we're just using this to wake the event loop up when we want to stop // See also https://github.com/libuv/libuv/issues/2173 evdata->stop = malloc(sizeof(uv_async_t)); uv_async_init(evdata->loop, evdata->stop, NULL); evdata->sigwinch = malloc(sizeof(uv_signal_t)); uv_signal_init(evdata->loop, evdata->sigwinch); uv_signal_start(evdata->sigwinch, el_sigwinch, SIGWINCH); evdata->sigwinch->data = t; return evdata; abort: free(evdata); return NULL; } static void el_destroy(void *data) { } static void el_run(void *data, TickitRunFlags flags) { EventLoopData *evdata = data; if(flags & TICKIT_RUN_NOHANG) uv_run(evdata->loop, UV_RUN_NOWAIT); else if(flags & TICKIT_RUN_ONCE) uv_run(evdata->loop, UV_RUN_ONCE); else uv_run(evdata->loop, UV_RUN_DEFAULT); } static void el_stop(void *data) { EventLoopData *evdata = data; uv_stop(evdata->loop); uv_async_send(evdata->stop); } static void fire_io_event(uv_poll_t *handle, int status, int events) { TickitWatch *watch = handle->data; if(events & UV_READABLE) { tickit_evloop_invoke_watch(watch, TICKIT_EV_FIRE); } } static bool el_io_read(void *data, int fd, TickitBindFlags flags, TickitWatch *watch) { EventLoopData *evdata = data; uv_poll_t *handle = malloc(sizeof(uv_poll_t)); if(!handle) return false; uv_poll_init(evdata->loop, handle, fd); uv_poll_start(handle, UV_READABLE, &fire_io_event); handle->data = watch; tickit_evloop_set_watch_data(watch, handle); return true; } static void el_cancel_io(void *data, TickitWatch *watch) { uv_poll_t *handle = tickit_evloop_get_watch_data(watch); uv_poll_stop(handle); uv_close((uv_handle_t *)handle, &destroy_handle); } static void fire_timer_event(uv_timer_t *handle) { TickitWatch *watch = handle->data; tickit_evloop_invoke_watch(watch, TICKIT_EV_FIRE); uv_timer_stop(handle); uv_close((uv_handle_t *)handle, &destroy_handle); } static bool el_timer(void *data, const struct timeval *at, TickitBindFlags flags, TickitWatch *watch) { EventLoopData *evdata = data; struct timeval now; gettimeofday(&now, NULL); long msec = (at->tv_sec - now.tv_sec) * 1000 + (at->tv_usec - now.tv_usec) / 1000; uv_timer_t *handle = malloc(sizeof(uv_timer_t)); if(!handle) return false; uv_timer_init(evdata->loop, handle); uv_timer_start(handle, &fire_timer_event, msec, 0); handle->data = watch; tickit_evloop_set_watch_data(watch, handle); return true; } static void el_cancel_timer(void *data, TickitWatch *watch) { uv_timer_t *handle = tickit_evloop_get_watch_data(watch); uv_timer_stop(handle); uv_close((uv_handle_t *)handle, &destroy_handle); } static void fire_later_event(uv_idle_t *handle) { TickitWatch *watch = handle->data; tickit_evloop_invoke_watch(watch, TICKIT_EV_FIRE); uv_idle_stop(handle); uv_close((uv_handle_t *)handle, &destroy_handle); } static bool el_later(void *data, TickitBindFlags flags, TickitWatch *watch) { EventLoopData *evdata = data; uv_idle_t *handle = malloc(sizeof(uv_idle_t)); if(!handle) return false; uv_idle_init(evdata->loop, handle); uv_idle_start(handle, &fire_later_event); handle->data = watch; tickit_evloop_set_watch_data(watch, handle); return true; } static void el_cancel_later(void *data, TickitWatch *watch) { uv_idle_t *handle = tickit_evloop_get_watch_data(watch); uv_idle_stop(handle); uv_close((uv_handle_t *)handle, &destroy_handle); } TickitEventHooks libuv_evhooks = { .init = el_init, .destroy = el_destroy, .run = el_run, .stop = el_stop, .io_read = el_io_read, .cancel_io = el_cancel_io, .timer = el_timer, .cancel_timer = el_cancel_timer, .later = el_later, .cancel_later = el_cancel_later, }; libtickit-0.3.4/include/tickit-evloop.h0000644000000000000000000000313413613430267016215 0ustar 00000000000000#ifdef __cplusplus extern "C" { #endif #ifndef __TICKIT_EVLOOP_H__ #define __TICKIT_EVLOOP_H__ /* * The contents of this file should be considered entirely experimental, and * subject to any change at any time. We make no API or ABI stability * guarantees at this time. */ typedef struct TickitWatch TickitWatch; /* opaque */ typedef struct { void *(*init)(Tickit *t, void *initdata); void (*destroy)(void *data); void (*run)(void *data, TickitRunFlags flags); void (*stop)(void *data); bool (*io_read)(void *data, int fd, TickitBindFlags flags, TickitWatch *watch); void (*cancel_io)(void *data, TickitWatch *watch); /* Below here is optional */ bool (*timer)(void *data, const struct timeval *at, TickitBindFlags flags, TickitWatch *watch); void (*cancel_timer)(void *data, TickitWatch *watch); bool (*later)(void *data, TickitBindFlags flags, TickitWatch *watch); void (*cancel_later)(void *data, TickitWatch *watch); } TickitEventHooks; /* Helper functions for eventloop implementations */ int tickit_evloop_next_timer_msec(Tickit *t); void tickit_evloop_invoke_timers(Tickit *t); void *tickit_evloop_get_watch_data(TickitWatch *watch); void tickit_evloop_set_watch_data(TickitWatch *watch, void *data); int tickit_evloop_get_watch_data_int(TickitWatch *watch); void tickit_evloop_set_watch_data_int(TickitWatch *watch, int data); void tickit_evloop_invoke_watch(TickitWatch *watch, TickitEventFlags flags); void tickit_evloop_sigwinch(Tickit *t); Tickit *tickit_new_with_evloop(TickitTerm *tt, TickitEventHooks *evhooks, void *initdata); #endif #ifdef __cplusplus } #endif libtickit-0.3.4/include/tickit-mockterm.h0000644000000000000000000000260413613430267016533 0ustar 00000000000000#ifdef __cplusplus extern "C" { #endif #ifndef __TICKIT_MOCKTERM_H__ #define __TICKIT_MOCKTERM_H__ /* * The contents of this file should be considered entirely experimental, and * subject to any change at any time. We make no API or ABI stability * guarantees at this time. */ #include "tickit.h" typedef struct { enum { LOG_GOTO = 1, LOG_PRINT, LOG_ERASECH, LOG_CLEAR, LOG_SCROLLRECT, LOG_SETPEN, } type; int val1, val2; // GOTO(line, col); ERASECH(count, moveend); SCROLLRECT(downward,rightward) const char *str; // PRINT TickitRect rect; // SCROLLRECT TickitPen *pen; // SETPEN } TickitMockTermLogEntry; /* A TickitMockTerm really is a TickitTerm */ typedef TickitTerm TickitMockTerm; TickitMockTerm *tickit_mockterm_new(int lines, int cols); void tickit_mockterm_destroy(TickitMockTerm *mt); void tickit_mockterm_resize(TickitMockTerm *mt, int newlines, int newcols); size_t tickit_mockterm_get_display_text(TickitMockTerm *mt, char *buffer, size_t len, int line, int col, int width); TickitPen *tickit_mockterm_get_display_pen(TickitMockTerm *mt, int line, int col); int tickit_mockterm_loglen(TickitMockTerm *mt); TickitMockTermLogEntry *tickit_mockterm_peeklog(TickitMockTerm *mt, int i); void tickit_mockterm_clearlog(TickitMockTerm *mt); void tickit_mockterm_get_position(TickitMockTerm *mt, int *line, int *col); #endif #ifdef __cplusplus } #endif libtickit-0.3.4/include/tickit-termdrv.h0000644000000000000000000000441713613430267016401 0ustar 00000000000000#ifdef __cplusplus extern "C" { #endif #ifndef __TICKIT_TERMDRV_H__ #define __TICKIT_TERMDRV_H__ /* * The contents of this file should be considered entirely experimental, and * subject to any change at any time. We make no API or ABI stability * guarantees at this time. */ #include "tickit.h" #include typedef struct { void (*attach)(TickitTermDriver *ttd, TickitTerm *tt); /* optional */ void (*destroy)(TickitTermDriver *ttd); void (*start)(TickitTermDriver *ttd); /* optional */ bool (*started)(TickitTermDriver *ttd); /* optional */ void (*stop)(TickitTermDriver *ttd); /* optional */ void (*pause)(TickitTermDriver *ttd); /* optional */ void (*resume)(TickitTermDriver *ttd); /* optional */ bool (*print)(TickitTermDriver *ttd, const char *str, size_t len); bool (*goto_abs)(TickitTermDriver *ttd, int line, int col); bool (*move_rel)(TickitTermDriver *ttd, int downward, int rightward); bool (*scrollrect)(TickitTermDriver *ttd, const TickitRect *rect, int downward, int rightward); bool (*erasech)(TickitTermDriver *ttd, int count, TickitMaybeBool moveend); bool (*clear)(TickitTermDriver *ttd); bool (*chpen)(TickitTermDriver *ttd, const TickitPen *delta, const TickitPen *final); bool (*getctl_int)(TickitTermDriver *ttd, TickitTermCtl ctl, int *value); bool (*setctl_int)(TickitTermDriver *ttd, TickitTermCtl ctl, int value); bool (*setctl_str)(TickitTermDriver *ttd, TickitTermCtl ctl, const char *value); int (*gotkey)(TickitTermDriver *ttd, TermKey *tk, const TermKeyKey *key); /* optional */ } TickitTermDriverVTable; struct TickitTermDriver { TickitTerm *tt; TickitTermDriverVTable *vtable; }; void *tickit_termdrv_get_tmpbuffer(TickitTermDriver *ttd, size_t len); void tickit_termdrv_write_str(TickitTermDriver *ttd, const char *str, size_t len); void tickit_termdrv_write_strf(TickitTermDriver *ttd, const char *fmt, ...); TickitPen *tickit_termdrv_current_pen(TickitTermDriver *ttd); /* * Function to construct a new TickitTerm directly from a TickitTermDriver */ TickitTerm *tickit_term_new_for_driver(TickitTermDriver *ttd); /* * Intended for "subclass" terminal methods, to obtain their custom driver * when given a TT instance */ TickitTermDriver *tickit_term_get_driver(TickitTerm *tt); #endif #ifdef __cplusplus } #endif libtickit-0.3.4/include/tickit.h0000644000000000000000000006266213613430267014726 0ustar 00000000000000#ifdef __cplusplus extern "C" { #endif #ifndef __TICKIT_H__ #define __TICKIT_H__ /* We'd quite like the timer*() functions */ #ifndef _DEFAULT_SOURCE # define _DEFAULT_SOURCE #endif #include #include #include #include #include #include #ifdef __GNUC__ # define DEPRECATED __attribute__((deprecated)) #else # define DEPRECATED #endif #define TICKIT_VERSION_MAJOR 0 #define TICKIT_VERSION_MINOR 3 #define TICKIT_VERSION_PATCH 4 int tickit_version_major(void); int tickit_version_minor(void); int tickit_version_patch(void); /* * Top-level object / structure types */ typedef struct TickitPen TickitPen; typedef struct TickitRectSet TickitRectSet; typedef struct TickitRenderBuffer TickitRenderBuffer; typedef struct TickitString TickitString; typedef struct TickitTerm TickitTerm; typedef struct TickitWindow TickitWindow; typedef struct Tickit Tickit; /* Opaque struct pointers required but not part of official API */ typedef struct TickitTermDriver TickitTermDriver; typedef struct { int top; int left; int lines; int cols; } TickitRect; /* * Enumerations */ typedef enum { TICKIT_BIND_FIRST = 1<<0, TICKIT_BIND_UNBIND = 1<<1, TICKIT_BIND_DESTROY = 1<<2, } TickitBindFlags; typedef enum { TICKIT_CTL_USE_ALTSCREEN = 1, TICKIT_N_CTLS } TickitCtl; typedef enum { TICKIT_CURSORSHAPE_BLOCK = 1, TICKIT_CURSORSHAPE_UNDER, TICKIT_CURSORSHAPE_LEFT_BAR, } TickitCursorShape; typedef enum { TICKIT_LINECAP_START = 0x01, TICKIT_LINECAP_END = 0x02, TICKIT_LINECAP_BOTH = 0x03, } TickitLineCaps; typedef enum { TICKIT_LINE_SINGLE = 1, TICKIT_LINE_DOUBLE = 2, TICKIT_LINE_THICK = 3, } TickitLineStyle; typedef enum { TICKIT_NO = 0, TICKIT_YES = 1, TICKIT_MAYBE = -1, } TickitMaybeBool; typedef enum { TICKIT_PEN_FG = 1, /* colour */ TICKIT_PEN_BG, /* colour */ TICKIT_PEN_BOLD, /* bool */ TICKIT_PEN_UNDER, /* number */ TICKIT_PEN_ITALIC, /* bool */ TICKIT_PEN_REVERSE, /* bool */ TICKIT_PEN_STRIKE, /* bool */ TICKIT_PEN_ALTFONT, /* number */ TICKIT_PEN_BLINK, /* bool */ TICKIT_N_PEN_ATTRS } TickitPenAttr; typedef enum { TICKIT_PEN_UNDER_NONE, TICKIT_PEN_UNDER_SINGLE, TICKIT_PEN_UNDER_DOUBLE, TICKIT_PEN_UNDER_WAVY, TICKIT_N_PEN_UNDERS } TickitPenUnderline; typedef enum { TICKIT_RUN_DEFAULT = 0, TICKIT_RUN_ONCE = 1<<0, TICKIT_RUN_NOHANG = 1<<1, TICKIT_RUN_NOSETUP = 1<<2, } TickitRunFlags; typedef enum { /* This is part of the API so additions must go at the end only */ TICKIT_TERMCTL_ALTSCREEN = 1, TICKIT_TERMCTL_CURSORVIS, TICKIT_TERMCTL_MOUSE, TICKIT_TERMCTL_CURSORBLINK, TICKIT_TERMCTL_CURSORSHAPE, TICKIT_TERMCTL_ICON_TEXT, TICKIT_TERMCTL_TITLE_TEXT, TICKIT_TERMCTL_ICONTITLE_TEXT, TICKIT_TERMCTL_KEYPAD_APP, TICKIT_TERMCTL_COLORS, // read-only TICKIT_N_TERMCTLS } TickitTermCtl; typedef enum { TICKIT_TERM_MOUSEMODE_OFF, TICKIT_TERM_MOUSEMODE_CLICK, TICKIT_TERM_MOUSEMODE_DRAG, TICKIT_TERM_MOUSEMODE_MOVE, } TickitTermMouseMode; typedef enum { TICKIT_TYPE_NONE, TICKIT_TYPE_BOOL, TICKIT_TYPE_INT, TICKIT_TYPE_STR, TICKIT_TYPE_COLOUR, // currently unused except by pen } TickitType; typedef enum { TICKIT_WINCTL_STEAL_INPUT = 1, TICKIT_WINCTL_FOCUS_CHILD_NOTIFY, TICKIT_WINCTL_CURSORVIS, TICKIT_WINCTL_CURSORBLINK, TICKIT_WINCTL_CURSORSHAPE, TICKIT_N_WINCTLS } TickitWindowCtl; typedef enum { TICKIT_WINDOW_HIDDEN = 1<<0, TICKIT_WINDOW_LOWEST = 1<<1, TICKIT_WINDOW_ROOT_PARENT = 1<<2, TICKIT_WINDOW_STEAL_INPUT = 1<<3, // Composite flag TICKIT_WINDOW_POPUP = TICKIT_WINDOW_ROOT_PARENT|TICKIT_WINDOW_STEAL_INPUT, } TickitWindowFlags; // TODO: this wants a name surely? enum { TICKIT_MOD_SHIFT = 0x01, TICKIT_MOD_ALT = 0x02, TICKIT_MOD_CTRL = 0x04, }; /* back-compat name */ typedef enum { TICKIT_PENTYPE_BOOL = TICKIT_TYPE_BOOL, TICKIT_PENTYPE_INT = TICKIT_TYPE_INT, TICKIT_PENTYPE_COLOUR = TICKIT_TYPE_COLOUR, } TickitPenAttrType; /* * Secondary structures */ typedef struct { size_t bytes; int codepoints; int graphemes; int columns; } TickitStringPos; /* * Event types */ typedef enum { TICKIT_EV_FIRE = (1 << 0), TICKIT_EV_UNBIND = (1 << 1), TICKIT_EV_DESTROY = (1 << 2), } TickitEventFlags; typedef struct { int lines, cols; } TickitResizeEventInfo; typedef enum { TICKIT_KEYEV_KEY = 1, TICKIT_KEYEV_TEXT, } TickitKeyEventType; typedef struct { TickitKeyEventType type; int mod; const char *str; } TickitKeyEventInfo; typedef enum { TICKIT_MOUSEEV_PRESS = 1, TICKIT_MOUSEEV_DRAG, TICKIT_MOUSEEV_RELEASE, TICKIT_MOUSEEV_WHEEL, TICKIT_MOUSEEV_DRAG_START = 0x101, TICKIT_MOUSEEV_DRAG_OUTSIDE, TICKIT_MOUSEEV_DRAG_DROP, TICKIT_MOUSEEV_DRAG_STOP, } TickitMouseEventType; enum { TICKIT_MOUSEWHEEL_UP = 1, TICKIT_MOUSEWHEEL_DOWN, }; typedef struct { TickitMouseEventType type; int button; int mod; int line, col; } TickitMouseEventInfo; typedef struct { TickitRect rect; TickitRect oldrect; } TickitGeomchangeEventInfo; typedef struct { TickitRect rect; TickitRenderBuffer *rb; } TickitExposeEventInfo; typedef enum { TICKIT_FOCUSEV_IN = 1, TICKIT_FOCUSEV_OUT, } TickitFocusEventType; typedef struct { TickitFocusEventType type; TickitWindow *win; } TickitFocusEventInfo; /* * Functions */ /* TickitPen */ TickitPen *tickit_pen_new(void); TickitPen *tickit_pen_new_attrs(TickitPenAttr attr, ...); TickitPen *tickit_pen_clone(const TickitPen *orig); TickitPen *tickit_pen_ref(TickitPen *pen); void tickit_pen_unref(TickitPen *pen); bool tickit_pen_has_attr(const TickitPen *pen, TickitPenAttr attr); bool tickit_pen_is_nonempty(const TickitPen *pen); bool tickit_pen_nondefault_attr(const TickitPen *pen, TickitPenAttr attr); bool tickit_pen_is_nondefault(const TickitPen *pen); bool tickit_pen_get_bool_attr(const TickitPen *pen, TickitPenAttr attr); void tickit_pen_set_bool_attr(TickitPen *pen, TickitPenAttr attr, bool val); int tickit_pen_get_int_attr(const TickitPen *pen, TickitPenAttr attr); void tickit_pen_set_int_attr(TickitPen *pen, TickitPenAttr attr, int val); int tickit_pen_get_colour_attr(const TickitPen *pen, TickitPenAttr attr); void tickit_pen_set_colour_attr(TickitPen *pen, TickitPenAttr attr, int value); typedef struct { uint8_t r, g, b; } TickitPenRGB8; bool tickit_pen_has_colour_attr_rgb8(const TickitPen *pen, TickitPenAttr attr); TickitPenRGB8 tickit_pen_get_colour_attr_rgb8(const TickitPen *pen, TickitPenAttr attr); void tickit_pen_set_colour_attr_rgb8(TickitPen *pen, TickitPenAttr attr, TickitPenRGB8 value); bool tickit_pen_set_colour_attr_desc(TickitPen *pen, TickitPenAttr attr, const char *value); void tickit_pen_clear_attr(TickitPen *pen, TickitPenAttr attr); void tickit_pen_clear(TickitPen *pen); bool tickit_pen_equiv_attr(const TickitPen *a, const TickitPen *b, TickitPenAttr attr); bool tickit_pen_equiv(const TickitPen *a, const TickitPen *b); void tickit_pen_copy_attr(TickitPen *dst, const TickitPen *src, TickitPenAttr attr); void tickit_pen_copy(TickitPen *dst, const TickitPen *src, bool overwrite); typedef int TickitPenEventFn(TickitPen *tt, TickitEventFlags flags, void *info, void *user); typedef enum { TICKIT_PEN_ON_DESTROY, TICKIT_PEN_ON_CHANGE, } TickitPenEvent; int tickit_pen_bind_event(TickitPen *tt, TickitPenEvent ev, TickitBindFlags flags, TickitPenEventFn *fn, void *user); void tickit_pen_unbind_event_id(TickitPen *tt, int id); TickitPenAttrType tickit_pen_attrtype(TickitPenAttr attr); const char *tickit_pen_attrname(TickitPenAttr attr); TickitPenAttr tickit_pen_lookup_attr(const char *name); /* TickitRect */ void tickit_rect_init_sized(TickitRect *rect, int top, int left, int lines, int cols); void tickit_rect_init_bounded(TickitRect *rect, int top, int left, int bottom, int right); static inline int tickit_rect_bottom(const TickitRect *rect) { return rect->top + rect->lines; } static inline int tickit_rect_right (const TickitRect *rect) { return rect->left + rect->cols; } void tickit_rect_translate(TickitRect *rect, int downward, int rightward); bool tickit_rect_intersect(TickitRect *dst, const TickitRect *a, const TickitRect *b); bool tickit_rect_intersects(const TickitRect *a, const TickitRect *b); bool tickit_rect_contains(const TickitRect *large, const TickitRect *small); int tickit_rect_add(TickitRect ret[3], const TickitRect *a, const TickitRect *b); int tickit_rect_subtract(TickitRect ret[4], const TickitRect *orig, const TickitRect *hole); /* TickitRectSet */ TickitRectSet *tickit_rectset_new(void); void tickit_rectset_destroy(TickitRectSet *trs); void tickit_rectset_clear(TickitRectSet *trs); size_t tickit_rectset_rects(const TickitRectSet *trs); size_t tickit_rectset_get_rect(const TickitRectSet *trs, size_t i, TickitRect *rects); size_t tickit_rectset_get_rects(const TickitRectSet *trs, TickitRect rects[], size_t n); void tickit_rectset_add(TickitRectSet *trs, const TickitRect *rect); void tickit_rectset_subtract(TickitRectSet *trs, const TickitRect *rect); void tickit_rectset_translate(TickitRectSet *trs, int downward, int rightward); bool tickit_rectset_intersects(const TickitRectSet *trs, const TickitRect *rect); bool tickit_rectset_contains(const TickitRectSet *trs, const TickitRect *rect); /* TickitString */ TickitString *tickit_string_new(const char *str, size_t len); TickitString *tickit_string_ref(TickitString *s); void tickit_string_unref(TickitString *s); const char *tickit_string_get(const TickitString *s); size_t tickit_string_len(const TickitString *s); /* TickitTerm */ TickitTerm *tickit_term_new(void); TickitTerm *tickit_term_new_for_termtype(const char *termtype); void tickit_term_destroy(TickitTerm *tt); // Internal use only for now but consider making it public API struct TickitTermBuilder { const char *termtype; TickitTermDriver *driver; const struct TickitTerminfoHook { const char *(*getstr)(const char *name, const char *value, void *data); void *data; } *ti_hook; }; TickitTerm *tickit_term_build(const struct TickitTermBuilder *builder); TickitTerm *tickit_term_ref(TickitTerm *tt); void tickit_term_unref(TickitTerm *tt); TickitTerm *tickit_term_open_stdio(void); const char *tickit_term_get_termtype(TickitTerm *tt); typedef void TickitTermOutputFunc(TickitTerm *tt, const char *bytes, size_t len, void *user); void tickit_term_set_output_fd(TickitTerm *tt, int fd); int tickit_term_get_output_fd(const TickitTerm *tt); void tickit_term_set_output_func(TickitTerm *tt, TickitTermOutputFunc *fn, void *user); void tickit_term_set_output_buffer(TickitTerm *tt, size_t len); void tickit_term_await_started_msec(TickitTerm *tt, long msec); void tickit_term_await_started_tv(TickitTerm *tt, const struct timeval *timeout); void tickit_term_flush(TickitTerm *tt); void tickit_term_pause(TickitTerm *tt); void tickit_term_resume(TickitTerm *tt); /* fd is allowed to be unset (-1); works abstractly */ void tickit_term_set_input_fd(TickitTerm *tt, int fd); int tickit_term_get_input_fd(const TickitTerm *tt); TickitMaybeBool tickit_term_get_utf8(const TickitTerm *tt); void tickit_term_set_utf8(TickitTerm *tt, bool utf8); void tickit_term_input_push_bytes(TickitTerm *tt, const char *bytes, size_t len); void tickit_term_input_readable(TickitTerm *tt); int tickit_term_input_check_timeout_msec(TickitTerm *tt); void tickit_term_input_wait_msec(TickitTerm *tt, long msec); void tickit_term_input_wait_tv(TickitTerm *tt, const struct timeval *timeout); void tickit_term_get_size(const TickitTerm *tt, int *lines, int *cols); void tickit_term_set_size(TickitTerm *tt, int lines, int cols); void tickit_term_refresh_size(TickitTerm *tt); void tickit_term_observe_sigwinch(TickitTerm *tt, bool observe); typedef int TickitTermEventFn(TickitTerm *tt, TickitEventFlags flags, void *info, void *user); typedef enum { TICKIT_TERM_ON_DESTROY, TICKIT_TERM_ON_RESIZE, TICKIT_TERM_ON_KEY, TICKIT_TERM_ON_MOUSE, } TickitTermEvent; int tickit_term_bind_event(TickitTerm *tt, TickitTermEvent ev, TickitBindFlags flags, TickitTermEventFn *fn, void *user); void tickit_term_unbind_event_id(TickitTerm *tt, int id); void tickit_term_print(TickitTerm *tt, const char *str); void tickit_term_printn(TickitTerm *tt, const char *str, size_t len); void tickit_term_printf(TickitTerm *tt, const char *fmt, ...); void tickit_term_vprintf(TickitTerm *tt, const char *fmt, va_list args); bool tickit_term_goto(TickitTerm *tt, int line, int col); void tickit_term_move(TickitTerm *tt, int downward, int rightward); bool tickit_term_scrollrect(TickitTerm *tt, TickitRect rect, int downward, int rightward); void tickit_term_chpen(TickitTerm *tt, const TickitPen *pen); void tickit_term_setpen(TickitTerm *tt, const TickitPen *pen); void tickit_term_clear(TickitTerm *tt); void tickit_term_erasech(TickitTerm *tt, int count, TickitMaybeBool moveend); bool tickit_term_getctl_int(TickitTerm *tt, TickitTermCtl ctl, int *value); bool tickit_term_setctl_int(TickitTerm *tt, TickitTermCtl ctl, int value); bool tickit_term_setctl_str(TickitTerm *tt, TickitTermCtl ctl, const char *value); void tickit_term_emit_key(TickitTerm *tt, TickitKeyEventInfo *info); void tickit_term_emit_mouse(TickitTerm *tt, TickitMouseEventInfo *info); const char *tickit_term_ctlname(TickitTermCtl ctl); TickitTermCtl tickit_term_lookup_ctl(const char *name); TickitType tickit_term_ctltype(TickitTermCtl ctl); /* String handling utilities */ int tickit_utf8_seqlen(long codepoint); /* Does NOT NUL-terminate the buffer */ size_t tickit_utf8_put(char *str, size_t len, long codepoint); size_t tickit_utf8_count(const char *str, TickitStringPos *pos, const TickitStringPos *limit); size_t tickit_utf8_countmore(const char *str, TickitStringPos *pos, const TickitStringPos *limit); size_t tickit_utf8_ncount(const char *str, size_t len, TickitStringPos *pos, const TickitStringPos *limit); size_t tickit_utf8_ncountmore(const char *str, size_t len, TickitStringPos *pos, const TickitStringPos *limit); // Some convenient mutators for TickitStringPos structs static inline void tickit_stringpos_zero(TickitStringPos *pos) { pos->bytes = pos->codepoints = pos->graphemes = pos->columns = 0; } #define INIT_TICKIT_STRINGPOS_LIMIT_NONE { .bytes = -1, .codepoints = -1, .graphemes = -1, .columns = -1 } static inline void tickit_stringpos_limit_none(TickitStringPos *pos) { pos->bytes = pos->codepoints = pos->graphemes = pos->columns = -1; } #define INIT_TICKIT_STRINGPOS_LIMIT_BYTES(v) { .bytes = (v), .codepoints = -1, .graphemes = -1, .columns = -1 } static inline void tickit_stringpos_limit_bytes(TickitStringPos *pos, size_t bytes) { pos->codepoints = pos->graphemes = pos->columns = -1; pos->bytes = bytes; } #define INIT_TICKIT_STRINGPOS_LIMIT_CODEPOINTS(v) { .bytes = -1, .codepoints = (v), .graphemes = -1, .columns = -1 } static inline void tickit_stringpos_limit_codepoints(TickitStringPos *pos, int codepoints) { pos->bytes = pos->graphemes = pos->columns = -1; pos->codepoints = codepoints; } #define INIT_TICKIT_STRINGPOS_LIMIT_GRAPHEMES(v) { .bytes = -1, .codepoints = -1, .graphemes = (v), .columns = -1 } static inline void tickit_stringpos_limit_graphemes(TickitStringPos *pos, int graphemes) { pos->bytes = pos->codepoints = pos->columns = -1; pos->graphemes = graphemes; } #define INIT_TICKIT_STRINGPOS_LIMIT_COLUMNS(v) { .bytes = -1, .codepoints = -1, .graphemes = -1, .columns = (v) } static inline void tickit_stringpos_limit_columns(TickitStringPos *pos, int columns) { pos->bytes = pos->codepoints = pos->graphemes = -1; pos->columns = columns; } int tickit_utf8_mbswidth(const char *str); int tickit_utf8_byte2col(const char *str, size_t byte); size_t tickit_utf8_col2byte(const char *str, int col); /* TickitRenderBuffer */ TickitRenderBuffer *tickit_renderbuffer_new(int lines, int cols); void tickit_renderbuffer_destroy(TickitRenderBuffer *rb); TickitRenderBuffer *tickit_renderbuffer_ref(TickitRenderBuffer *rb); void tickit_renderbuffer_unref(TickitRenderBuffer *rb); void tickit_renderbuffer_get_size(const TickitRenderBuffer *rb, int *lines, int *cols); void tickit_renderbuffer_translate(TickitRenderBuffer *rb, int downward, int rightward); void tickit_renderbuffer_clip(TickitRenderBuffer *rb, TickitRect *rect); void tickit_renderbuffer_mask(TickitRenderBuffer *rb, TickitRect *mask); bool tickit_renderbuffer_has_cursorpos(const TickitRenderBuffer *rb); void tickit_renderbuffer_get_cursorpos(const TickitRenderBuffer *rb, int *line, int *col); void tickit_renderbuffer_goto(TickitRenderBuffer *rb, int line, int col); void tickit_renderbuffer_ungoto(TickitRenderBuffer *rb); void tickit_renderbuffer_setpen(TickitRenderBuffer *rb, const TickitPen *pen); void tickit_renderbuffer_reset(TickitRenderBuffer *rb); void tickit_renderbuffer_save(TickitRenderBuffer *rb); void tickit_renderbuffer_savepen(TickitRenderBuffer *rb); void tickit_renderbuffer_restore(TickitRenderBuffer *rb); void tickit_renderbuffer_skip_at(TickitRenderBuffer *rb, int line, int col, int cols); void tickit_renderbuffer_skip(TickitRenderBuffer *rb, int cols); void tickit_renderbuffer_skip_to(TickitRenderBuffer *rb, int col); void tickit_renderbuffer_skiprect(TickitRenderBuffer *rb, TickitRect *rect); int tickit_renderbuffer_text_at(TickitRenderBuffer *rb, int line, int col, const char *text); int tickit_renderbuffer_textn_at(TickitRenderBuffer *rb, int line, int col, const char *text, size_t len); int tickit_renderbuffer_text(TickitRenderBuffer *rb, const char *text); int tickit_renderbuffer_textn(TickitRenderBuffer *rb, const char *text, size_t len); int tickit_renderbuffer_textf_at(TickitRenderBuffer *rb, int line, int col, const char *fmt, ...); int tickit_renderbuffer_vtextf_at(TickitRenderBuffer *rb, int line, int col, const char *fmt, va_list args); int tickit_renderbuffer_textf(TickitRenderBuffer *rb, const char *fmt, ...); int tickit_renderbuffer_vtextf(TickitRenderBuffer *rb, const char *fmt, va_list args); void tickit_renderbuffer_erase_at(TickitRenderBuffer *rb, int line, int col, int cols); void tickit_renderbuffer_erase(TickitRenderBuffer *rb, int cols); void tickit_renderbuffer_erase_to(TickitRenderBuffer *rb, int col); void tickit_renderbuffer_eraserect(TickitRenderBuffer *rb, TickitRect *rect); void tickit_renderbuffer_clear(TickitRenderBuffer *rb); void tickit_renderbuffer_char_at(TickitRenderBuffer *rb, int line, int col, long codepoint); void tickit_renderbuffer_char(TickitRenderBuffer *rb, long codepoint); void tickit_renderbuffer_hline_at(TickitRenderBuffer *rb, int line, int startcol, int endcol, TickitLineStyle style, TickitLineCaps caps); void tickit_renderbuffer_vline_at(TickitRenderBuffer *rb, int startline, int endline, int col, TickitLineStyle style, TickitLineCaps caps); void tickit_renderbuffer_copyrect(TickitRenderBuffer *rb, const TickitRect *dest, const TickitRect *src); void tickit_renderbuffer_moverect(TickitRenderBuffer *rb, const TickitRect *dest, const TickitRect *src); void tickit_renderbuffer_flush_to_term(TickitRenderBuffer *rb, TickitTerm *tt); void tickit_renderbuffer_blit(TickitRenderBuffer *dst, TickitRenderBuffer *src); // This API is still somewhat experimental typedef struct { char north; char south; char east; char west; } TickitRenderBufferLineMask; int tickit_renderbuffer_get_cell_active(TickitRenderBuffer *rb, int line, int col); size_t tickit_renderbuffer_get_cell_text(TickitRenderBuffer *rb, int line, int col, char *buffer, size_t len); TickitRenderBufferLineMask tickit_renderbuffer_get_cell_linemask(TickitRenderBuffer *rb, int line, int col); // returns a direct pointer - do not free or modify TickitPen *tickit_renderbuffer_get_cell_pen(TickitRenderBuffer *rb, int line, int col); struct TickitRenderBufferSpanInfo { bool is_active; int n_columns; char *text; size_t len; TickitPen *pen; }; // returns the text length or -1 on error size_t tickit_renderbuffer_get_span(TickitRenderBuffer *rb, int line, int startcol, struct TickitRenderBufferSpanInfo *info, char *buffer, size_t len); /* Window */ TickitWindow *tickit_window_new_root(TickitTerm *term); TickitWindow *tickit_window_new(TickitWindow *parent, TickitRect rect, TickitWindowFlags flags); TickitWindow *tickit_window_parent(const TickitWindow *win); TickitWindow *tickit_window_root(const TickitWindow *win); size_t tickit_window_children(const TickitWindow *win); size_t tickit_window_get_children(const TickitWindow *win, TickitWindow *children[], size_t n); TickitTerm *tickit_window_get_term(const TickitWindow *win); void tickit_window_close(TickitWindow *win); void tickit_window_destroy(TickitWindow *win); TickitWindow *tickit_window_ref(TickitWindow *win); void tickit_window_unref(TickitWindow *win); typedef int TickitWindowEventFn(TickitWindow *win, TickitEventFlags flags, void *info, void *user); typedef enum { TICKIT_WINDOW_ON_DESTROY, TICKIT_WINDOW_ON_GEOMCHANGE, TICKIT_WINDOW_ON_EXPOSE, TICKIT_WINDOW_ON_FOCUS, TICKIT_WINDOW_ON_KEY, TICKIT_WINDOW_ON_MOUSE, } TickitWindowEvent; int tickit_window_bind_event(TickitWindow *win, TickitWindowEvent ev, TickitBindFlags flags, TickitWindowEventFn *fn, void *user); void tickit_window_unbind_event_id(TickitWindow *win, int id); void tickit_window_raise(TickitWindow *win); void tickit_window_raise_to_front(TickitWindow *win); void tickit_window_lower(TickitWindow *win); void tickit_window_lower_to_back(TickitWindow *win); void tickit_window_show(TickitWindow *win); void tickit_window_hide(TickitWindow *win); bool tickit_window_is_visible(TickitWindow *win); TickitRect tickit_window_get_geometry(const TickitWindow *win); TickitRect tickit_window_get_abs_geometry(const TickitWindow *win); #define tickit_window_top(win) (tickit_window_get_geometry(win)).top #define tickit_window_left(win) (tickit_window_get_geometry(win)).left #define tickit_window_lines(win) (tickit_window_get_geometry(win)).lines #define tickit_window_cols(win) (tickit_window_get_geometry(win)).cols int tickit_window_bottom(const TickitWindow *win); int tickit_window_right(const TickitWindow *win); void tickit_window_resize(TickitWindow *win, int lines, int cols); void tickit_window_reposition(TickitWindow *win, int top, int left); void tickit_window_set_geometry(TickitWindow *win, TickitRect rect); TickitPen *tickit_window_get_pen(const TickitWindow *win); void tickit_window_set_pen(TickitWindow *win, TickitPen *pen); void tickit_window_expose(TickitWindow *win, const TickitRect *exposed); void tickit_window_flush(TickitWindow *win); bool tickit_window_scrollrect(TickitWindow *win, const TickitRect *rect, int downward, int rightward, TickitPen *pen); bool tickit_window_scroll(TickitWindow *win, int downward, int rightward); bool tickit_window_scroll_with_children(TickitWindow *win, int downward, int rightward); void tickit_window_set_cursor_position(TickitWindow *win, int line, int col); void tickit_window_set_cursor_visible(TickitWindow *win, bool visible); void tickit_window_set_cursor_shape(TickitWindow *win, TickitCursorShape shape); void tickit_window_take_focus(TickitWindow *win); bool tickit_window_is_focused(const TickitWindow *win); void tickit_window_set_focus_child_notify(TickitWindow *win, bool notify); bool tickit_window_getctl_int(TickitWindow *win, TickitWindowCtl ctl, int *value); bool tickit_window_setctl_int(TickitWindow *win, TickitWindowCtl ctl, int value); bool tickit_window_is_steal_input(const TickitWindow *win); void tickit_window_set_steal_input(TickitWindow *win, bool steal); const char *tickit_window_ctlname(TickitWindowCtl ctl); TickitWindowCtl tickit_window_lookup_ctl(const char *name); TickitType tickit_window_ctltype(TickitWindowCtl ctl); /* Main object */ typedef int TickitCallbackFn(Tickit *t, TickitEventFlags flags, void *info, void *user); Tickit *tickit_new_for_term(TickitTerm *tt); Tickit *tickit_new_stdio(void); Tickit *tickit_ref(Tickit *t); void tickit_unref(Tickit *t); TickitTerm *tickit_get_term(Tickit *t); TickitWindow *tickit_get_rootwin(Tickit *t); void tickit_run(Tickit *t); void tickit_stop(Tickit *t); void tickit_tick(Tickit *t, TickitRunFlags flags); bool tickit_getctl_int(Tickit *tt, TickitCtl ctl, int *value); bool tickit_setctl_int(Tickit *tt, TickitCtl ctl, int value); const char *tickit_ctlname(TickitCtl ctl); TickitCtl tickit_lookup_ctl(const char *name); TickitType tickit_ctltype(TickitCtl ctl); /* TODO: Consider OUT and HUP conditions too */ void *tickit_watch_io_read(Tickit *t, int fd, TickitBindFlags flags, TickitCallbackFn *fn, void *user); void *tickit_watch_timer_after_msec(Tickit *t, int msec, TickitBindFlags flags, TickitCallbackFn *fn, void *user); void *tickit_watch_timer_after_tv(Tickit *t, const struct timeval *after, TickitBindFlags flags, TickitCallbackFn *fn, void *user); void *tickit_watch_timer_at_epoch(Tickit *t, time_t at, TickitBindFlags flags, TickitCallbackFn *fn, void *user); void *tickit_watch_timer_at_tv(Tickit *t, const struct timeval *at, TickitBindFlags flags, TickitCallbackFn *fn, void *user); void *tickit_watch_later(Tickit *t, TickitBindFlags flags, TickitCallbackFn *fn, void *user); void tickit_watch_cancel(Tickit *t, void *watch); /* Entirely undocumented right now */ void tickit_hook_terminfo(Tickit *t, const char *(*getstr)(const char *name, const char *value, void *data), void *data); /* Debug support */ void tickit_debug_init(void); extern bool tickit_debug_enabled; void tickit_debug_logf(const char *flag, const char *fmt, ...); void tickit_debug_vlogf(const char *flag, const char *fmt, va_list args); void tickit_debug_set_func(void (*func)(const char *str, void *data), void *data); void tickit_debug_set_fh(FILE *fh); bool tickit_debug_open(const char *path); #endif #ifdef __cplusplus } #endif libtickit-0.3.4/man/also0000644000000000000000000001250013613430267013261 0ustar 00000000000000tickit_term_new_for_termtype.3 = tickit_term_new.3 tickit_term_unref.3 = tickit_term_ref.3 tickit_term_unbind_event_id.3 = tickit_term_bind_event.3 tickit_term_get_output_fd.3 = tickit_term_set_output_fd.3 tickit_term_get_input_fd.3 = tickit_term_set_input_fd.3 tickit_term_set_size.3 = tickit_term_get_size.3 tickit_term_refresh_size.3 = tickit_term_get_size.3 tickit_term_move.3 = tickit_term_goto.3 tickit_term_printn.3 = tickit_term_print.3 tickit_term_printf.3 = tickit_term_print.3 tickit_term_vprintf.3 = tickit_term_print.3 tickit_term_setpen.3 = tickit_term_chpen.3 tickit_term_get_utf8.3 = tickit_term_set_utf8.3 tickit_term_getctl_int.3 = tickit_term_setctl_int.3 tickit_term_setctl_str.3 = tickit_term_setctl_int.3 tickit_term_input_wait_tv.3 = tickit_term_input_wait_msec.3 tickit_term_await_started_tv.3 = tickit_term_await_started_msec.3 tickit_term_emit_mouse.3 = tickit_term_emit_key.3 tickit_term_lookup_ctl.3 = tickit_term_ctlname.3 tickit_term_ctltype.3 = tickit_term_ctlname.3 tickit_term_resume.3 = tickit_term_pause.3 tickit_pen_new_attrs.3 = tickit_pen_new.3 tickit_pen_clone.3 = tickit_pen_new.3 tickit_pen_unref.3 = tickit_pen_ref.3 tickit_pen_unbind_event_id.3 = tickit_pen_bind_event.3 tickit_pen_nondefault_attr.3 = tickit_pen_has_attr.3 tickit_pen_is_nondefault.3 = tickit_pen_is_nonempty.3 tickit_pen_set_bool_attr.3 = tickit_pen_get_bool_attr.3 tickit_pen_set_int_attr.3 = tickit_pen_get_int_attr.3 tickit_pen_set_colour_attr.3 = tickit_pen_get_colour_attr.3 tickit_pen_set_colour_attr_desc.3 = tickit_pen_get_colour_attr.3 tickit_pen_set_colour_attr_rgb8.3 = tickit_pen_get_colour_attr_rgb8.3 tickit_pen_has_colour_attr_rgb8.3 = tickit_pen_get_colour_attr_rgb8.3 tickit_pen_copy_attr.3 = tickit_pen_copy.3 tickit_pen_lookup_attr.3 = tickit_pen_attrname.3 tickit_pen_equiv.3 = tickit_pen_equiv_attr.3 tickit_pen_clear_attr.3 = tickit_pen_clear.3 tickit_utf8_byte2col.3 = tickit_utf8_mbswidth.3 tickit_utf8_col2byte.3 = tickit_utf8_mbswidth.3 tickit_rect_init_bounded.3 = tickit_rect_init_sized.3 tickit_rect_right.3 = tickit_rect_bottom.3 tickit_rectset_destroy.3 = tickit_rectset_new.3 tickit_rectset_get_rect.3 = tickit_rectset_rects.3 tickit_rectset_get_rects.3 = tickit_rectset_rects.3 tickit_string_unref.3 = tickit_string_ref.3 tickit_renderbuffer_unref.3 = tickit_renderbuffer_ref.3 tickit_renderbuffer_mask.3 = tickit_renderbuffer_clip.3 tickit_renderbuffer_restore.3 = tickit_renderbuffer_save.3 tickit_renderbuffer_savepen.3 = tickit_renderbuffer_save.3 tickit_renderbuffer_ungoto.3 = tickit_renderbuffer_goto.3 tickit_renderbuffer_has_cursorpos.3 = tickit_renderbuffer_get_cursorpos.3 tickit_renderbuffer_skip_to.3 = tickit_renderbuffer_skip.3 tickit_renderbuffer_skip_at.3 = tickit_renderbuffer_skip.3 tickit_renderbuffer_skiprect.3 = tickit_renderbuffer_skip.3 tickit_renderbuffer_text_at.3 = tickit_renderbuffer_text.3 tickit_renderbuffer_textn.3 = tickit_renderbuffer_text.3 tickit_renderbuffer_textn_at.3 = tickit_renderbuffer_text.3 tickit_renderbuffer_textf.3 = tickit_renderbuffer_text.3 tickit_renderbuffer_textf_at.3 = tickit_renderbuffer_text.3 tickit_renderbuffer_vtextf.3 = tickit_renderbuffer_text.3 tickit_renderbuffer_vtextf_at.3 = tickit_renderbuffer_text.3 tickit_renderbuffer_erase_to.3 = tickit_renderbuffer_erase.3 tickit_renderbuffer_erase_at.3 = tickit_renderbuffer_erase.3 tickit_renderbuffer_clear.3 = tickit_renderbuffer_eraserect.3 tickit_renderbuffer_char_at.3 = tickit_renderbuffer_char.3 tickit_renderbuffer_vline_at.3 = tickit_renderbuffer_hline_at.3 tickit_renderbuffer_moverect.3 = tickit_renderbuffer_copyrect.3 tickit_window_unref.3 = tickit_window_ref.3 tickit_window_root.3 = tickit_window_parent.3 tickit_window_get_children.3 = tickit_window_children.3 tickit_window_unbind_event_id.3 = tickit_window_bind_event.3 tickit_window_raise_to_front.3 = tickit_window_raise.3 tickit_window_lower.3 = tickit_window_raise.3 tickit_window_lower_to_back.3 = tickit_window_raise.3 tickit_window_hide.3 = tickit_window_show.3 tickit_window_is_visible.3 = tickit_window_show.3 tickit_window_top.3 = tickit_window_get_geometry.3 tickit_window_left.3 = tickit_window_get_geometry.3 tickit_window_lines.3 = tickit_window_get_geometry.3 tickit_window_cols.3 = tickit_window_get_geometry.3 tickit_window_bottom.3 = tickit_window_get_geometry.3 tickit_window_right.3 = tickit_window_get_geometry.3 tickit_window_resize.3 = tickit_window_set_geometry.3 tickit_window_reposition.3 = tickit_window_set_geometry.3 tickit_window_set_pen.3 = tickit_window_get_pen.3 tickit_window_is_focused.3 = tickit_window_take_focus.3 tickit_window_set_cursor_visible.3 = tickit_window_set_cursor_position.3 tickit_window_set_cursor_shape.3 = tickit_window_set_cursor_position.3 tickit_window_scrollrect.3 = tickit_window_scroll.3 tickit_window_scroll_with_children.3 = tickit_window_scroll.3 tickit_window_is_steal_input.3 = tickit_window_set_steal_input.3 tickit_window_getctl_int.3 = tickit_window_setctl_int.3 tickit_window_lookup_ctl.3 = tickit_window_ctlname.3 tickit_window_ctltype.3 = tickit_window_ctlname.3 tickit_unref.3 = tickit_ref.3 tickit_stop.3 = tickit_run.3 tickit_tick.3 = tickit_run.3 tickit_getctl_int.3 = tickit_setctl_int.3 tickit_watch_timer_after_tv.3 = tickit_watch_timer_after_msec.3 tickit_watch_timer_at_tv.3 = tickit_watch_timer_at_epoch.3 tickit_lookup_ctl.3 = tickit_ctlname.3 tickit_ctltype.3 = tickit_ctlname.3 tickit_debug_vlogf.3 = tickit_debug_logf.3 libtickit-0.3.4/man/tickit.70000644000000000000000000001561213613430267013766 0ustar 00000000000000.TH TICKIT 7 .SH NAME tickit \- Terminal Interface Construction KIT .SH SYNOPSIS .EX .B #include .sp .BI "typedef struct " Tickit ; .EE .sp .SH DESCRIPTION \fItickit\fP is a library for building full-screen interactive programs that use a terminal interface. A program using this library would start by creating a toplevel \fBTickit\fP instance, from which one or more divisions of the terminal area, called "windows" are created. These form a heirarchial tree that subdivides the content area into independent regions that can be managed by different parts of the program structure. Each window can react to input events such as keyboard or mouse interaction. .PP As well as creating the initial root window, the toplevel \fBTickit\fP instance also performs a few other jobs for the containing program. It can act as a containing event loop for the program, performing IO multiplexing tasks both for \fItickit\fP's own needs and the needs of the program as a whole. .SH FUNCTIONS A new toplevel instance is created by using \fBtickit_new_stdio\fP(3). A toplevel instance stores a reference count to make it easier for applications to manage its lifetime. A new toplevel instance starts with a count of one, and it can be adjusted using \fBtickit_ref\fP(3) and \fBtickit_unref\fP(3). When the count reaches zero the instance is destroyed. .PP The toplevel instance manages a tree of \fBTickitWindow\fP instances. The root of this tree is obtained by \fBtickit_get_rootwin\fP(3) and thereafter can be divided further by other functions on the window, described more in \fBtickit_window\fP(7). .PP The \fBTickitTerm\fP instance behind the toplevel instance can be obtained by \fBtickit_get_term\fP(3), and is described more in \fBtickit_term\fP(7). .PP Event handling callback functions can be installed to be called at a later time, by using \fBtickit_watch_timer_after_msec\fP(3), \fBtickit_watch_timer_after_tv\fP(3), or \fBtickit_watch_later\fP(3). The main IO event loop is controlled using \fBtickit_run\fP(3) and \fBtickit_stop\fP(3). .PP The compile-time and run-time version of the library can be inspected using the macros and functions described in \fBtickit_version\fP(7). .SH "TYPICAL STRUCTURE" A typical program using this library would start by creating the toplevel instance, by calling \fBtickit_new_stdio\fP(3), then obtain its root window on it by calling \fBtickit_get_rootwin\fP(3). This root window can then be sub-divided into regions of interest by calling \fBtickit_window_new\fP(3) to build a tree of windows. Window can then have some event handlers attached by calling \fBtickit_window_bind_event\fP(3) - each window will need to handle the \fBTICKIT_WINDOW_ON_EXPOSE\fP event, but might also wish to handle other kinds like geometry change for dynamic resizing, or keyboard or mouse to react to user input. Finally, once the intial window tree is created, the program would enter the main event loop by invoking \fBtickit_run\fP(3). .SH "COMMON TYPES" The \fIflags\fP argument to the various \fBtickit_..._bind_event\fP() functions should be zero, or a bitmask of the following constants. .sp .EX .B typedef enum { .BR " TICKIT_BIND_FIRST" , .BR " TICKIT_BIND_UNBIND" , .BR " TICKIT_BIND_DESTROY" , .BI "} " TickitBindFlags ; .EE .sp .PP \fBTICKIT_BIND_FIRST\fP indicates that this handler should be inserted at the start of the list, rather than the default position at the end. .PP \fBTICKIT_BIND_UNBIND\fP indicates that this handler should also be invoked at the time it is unbound, either due to a specific call to the \fBtickit_..._unbind_event\fP() function, or because the bound object is being destroyed. .PP \fBTICKIT_BIND_DESTROY\fP indicates that this handler should also be invoked at the time that the bound object is being destroyed. .PP Some API functions take or return the following enum type, to represent a tri-state extended boolean concept of true, false, or some third condition typically indicating a "don't care" or "unknown" state; the exact semantics will vary between specific uses and should be documented specifically. .sp .EX .B typedef enum { .BR " TICKIT_YES" " = 1," .BR " TICKIT_NO" " = 0," .BR " TICKIT_MAYBE" " = -1", .BI "} " TickitMaybeBool ; .EE .PP The various \fBtickit_*_ctltype\fP() and \fBtickit_pen_attrtype\fP(3) functions return the following enum type, to indicate what type of value each individual control or attribute takes. .sp .EX .B typedef enum { .BR " TICKIT_TYPE_NONE" , .BR " TICKIT_TYPE_BOOL" , .BR " TICKIT_TYPE_INT" , .BR " TICKIT_TYPE_COLOUR" , .BI "} " TickitType ; .EE .SH "COMMON EVENTS" Every object instance that supports events supports the following type of event, in addition to the specific ones listed for that kind of object: .TP .B TICKIT_..._ON_DESTROY Invoked when the object instance is being destroyed. This will be the last time the application can use the stored \fIdata\fP argument; it may perform any resource reclaiming operations that are required at this time. .SH "EVENT FLAGS" When an event handler function is invoked, it is passed a bitmask of flags to indicate the reason for its invocation. .sp .EX .B typedef enum { .BR " TICKIT_EV_FIRE" , .BR " TICKIT_EV_UNBIND" , .BR " TICKIT_EV_DESTROY" , .BI "} " TickitEventFlags ; .EE .TP .B TICKIT_EV_FIRE This handler is being invoked because its associated event has occurred. The \fIinfo\fP pointer will point to a structure containing the relevant information. .TP .B TICKIT_EV_UNBIND This handler is being invoked because it is being removed from the object. This will only be observed if it was bound with the \fBTICKIT_BIND_UNBIND\fP flag. The \fIinfo\fP pointer will be \fBNULL\fP. .TP .B TICKIT_EV_DESTROY This handler is being invoked because the object instance itself is being destroyed. This will be observed if it was bound with the \fBTICKIT_BIND_DESTROY\fP flag, or because it is bound to the \fBTICKIT_..._ON_DESTROY\fP event. The \fIinfo\fP pointer will be \fBNULL\fP. .IP Any event handlers for this event will be invoked in reverse order; the newest is run first and the oldest last. .SH CONTROLS A toplevel instance has a number of runtime-configuration control options that affect its behaviour. These can be set using \fBtickit_setctl_int\fP(3), and queried using \fBtickit_getctl_int\fP(3). The individual controls have human-readable string names that can be obtained by \fBtickit_ctlname\fP(3) and searched by name using \fBtickit_lookup_ctl\fP(3). The type of a control option can be queried using \fBtickit_ctltype\fP(3). .PP The options are given in an enumeration called \fBTickitCtl\fP. The following control values are recognised: .in .TP .B TICKIT_CTL_USE_ALTSCREEN (bool) The value is a boolean indicating whether the instance will activate the terminal alternate screen buffer mode when started. .SH "SEE ALSO" .BR tickit_window (7), .BR tickit_term (7), .BR tickit_pen (7), .BR tickit_rect (7), .BR tickit_rectset (7), .BR tickit_renderbuffer (7), .BR tickit_string (7), .BR tickit_utf8_count (3), .BR tickit_version (7) libtickit-0.3.4/man/tickit_ctlname.30000644000000000000000000000255313613430267015465 0ustar 00000000000000.TH TICKIT_CTLNAME 3 .SH NAME tickit_ctlname \- return the name of a toplevel instance control .SH SYNOPSIS .EX .B #include .sp .BI "const char * tickit_ctlname(TickitCtl " ctl ); .BI "TickitCtl tickit_lookup_ctl(const char * " name ); .sp .BI "TickitType tickit_ctltype(TickitCtl " ctl ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_ctlname\fP() returns the name of the given toplevel instance control. These names are the lowercase strings given in \fBtickit\fP(7). .PP \fBtickit_lookup_ctl\fP() returns the control constant for a given name. If the name is not recognised then -1 is returned. .PP \fBtickit_ctltype\fP() returns the type of the given instance control, as one of the following constants: .in .TP .B TICKIT_TYPE_BOOL The value is an integer representing a simple boolean choice, where any nonzero value is true. Use \fBtickit_setctl_int\fP(3) and \fBtickit_getctl_int\fP(3). .TP .B TICKIT_TYPE_INT The value is an integer representing a quantity, or a value in some particular enumeration. Use \fBtickit_setctl_int\fP(3) and \fBtickit_getctl_int\fP(3). .PP For unrecognised control types, the function returns 0. .SH "RETURN VALUE" \fBtickit_ctlname\fP() returns a constant string pointer. \fBtickit_lookup_ctl\fP() returns an control constant or -1. \fBtickit_ctltype\fP() returns a control type constant or 0. .SH "SEE ALSO" .BR tickit (7) libtickit-0.3.4/man/tickit_debug.70000644000000000000000000000644713613430267015142 0ustar 00000000000000.TH TICKIT_DEBUG 7 .SH NAME Tickit_Debug \- debug logging support for \fIlibtickit\fP .SH SYNOPSIS .EX $ TICKIT_DEBUG_FLAGS=W TICKIT_DEBUG_FILE=tickit.log a-tickit-program .EE .SH DESCRIPTION The \fIlibtickit\fP library contains a debug logging system that other parts of the library use to report on interesting events and behaviours, that may help developers to observe internal details and assist in the development and debugging of applications that use the library. .PP Debug messages themselves each have a flag name, which is a short string identifying the library component or event that caused that message. A given set of these flags can be enabled during a debugging session, causing only those messages to be printed. Messages pertaining to flags that are not enabled will not be printed. .SH FLAGS Each flag name starts with an upper-case letter indicating the library component it relates to, then lower-case letters to indicate the particular kind of event or message within that component. .SS B These messages relate to \fBTickitRenderBuffer\fP (tickit_renderbuffer(7)). .TP \f(CwBd\fP Drawing operations such as \fBtickit_renderbuffer_text\fP(3). .TP \f(CwBf\fP Flushing .TP \f(CwBs\fP Stack state save and restore. .TP \f(CwBt\fP .SS I These messages relate to input-system events. .TP \f(CwIk\fP Keyboard keypresses. .TP \f(CwIm\fP Mouse movement and button or wheel events. .TP \f(CwIr\fP Terminal resize. Transformations (translation, clipping and masking). .SS W These messages relate to windows. .TP \f(CwW*\fP Window destroyed. .TP \f(CwWd\fP Damage to root window. .TP \f(CwWx\fP Window is exposed. .TP \f(CwWh\fP Window hierarchy changed. .TP \f(CwWs\fP Window scrolls. .TP \f(CwWsr\fP Window propagates a scrollrect to its underlying terminal. .SH ENVIRONMENT The debugging support is controlled by the following environment variables, which should be set before the application starts, or early during its initialisation before it has invoked any \fIlibtickit\fP functions. .TP \f(CwTICKIT_DEBUG_FLAGS\fP A comma-separated list of the flags or flag categories to enable for printing. Entire categories of flags can be enabled by list just the initial capital letter of its name. All the flags can be enabled at once by setting the name \f(Cw*\fP. .TP \f(CwTICKIT_DEBUG_FILE\fP Gives the name of a file to open in append mode to write logging lines to. .TP \f(CwTICKIT_DEBUG_FD\fP Gives a file descriptor number to write logging lines to. .PP One technique that may be useful to observe logging in real-time while the application runs is to arrange for the application to run in a new terminal but have it log debugging output to an existing one. This may be achieved using .sp .EX $ TICKIT_DEBUG_FD=3 TICKIT_DEBUG_FLAGS=... $TERM my-tickit-app 3>&2 .EE .sp This requests that \fIlibtickit\fP log to file descriptor 3, which has been created by dup(2)ing the original shell's standard output. Debug logging will therefore be printed to the existing terminal where this command was executed, while the application itself draws its output to the newly-created one. .SH FUNCTIONS The debug system is intialised by calling \fBtickit_debug_init\fP(3). Messages can be logged using \fBtickit_debug_logf\fP(3) and \fBtickit_debug_vlogf\fP(3). .SH "SEE ALSO" .BR tickit (7), .BR tickit_term (7), .BR tickit_renderbuffer (7), .BR tickit_window (7) libtickit-0.3.4/man/tickit_debug_init.30000644000000000000000000000207713613430267016154 0ustar 00000000000000.TH TICKIT_DEBUG_INIT 3 .SH NAME tickit_debug_init \- initialise the debugging system .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_debug_init(void);" .sp .BI "bool " tickit_debug_enabled ; .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_debug_init\fP() initialises the \fIlibtickit\fP debugging system. It reads the values of the environment variables and sets up the output filehandle, if enabled. It also has a side-effect on the value of the \fItickit_debug_enabled\fP global variable, setting it to the correct value. Applications that wish to read this variable to control their output of debugging messages may wish to call this function on startup, to ensure it takes the correct value. .PP \fBtickit_debug_init\fP() is guarded internally so that it is safe to call multiple times; there is no downside to opportunistically invoking it on startup anyway, even if some other part of the application may also have done so. .SH "RETURN VALUE" This function returns no value. .SH "SEE ALSO" .BR tickit_debug_logf (3), .BR tickit_debug (7), .BR tickit (7) libtickit-0.3.4/man/tickit_debug_logf.30000644000000000000000000000162613613430267016137 0ustar 00000000000000.TH TICKIT_DEBUG_LOGF 3 .SH NAME tickit_debug_logf, tickit_debug_vlogf \- emit debugging log messages .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_debug_logf(const char *" flag , .BI " const char *" fmt ", ...);" .BI "void tickit_debug_vlogf(const char *" flag , .BI " const char *" fmt ", va_list " args ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_debug_logf\fP() emits a debug logging message, by creating a string using the given format string and argument list in the style of \fBsprintf\fP(3). If the requested message flag is enabled, this will be appended to the log output along with a timestamp and the name of the flag itself. .PP \fBtickit_debug_vlogf\fP() works analogously, except that it takes a single \fIva_list\fP for the arguments to format. .SH "RETURN VALUE" These functions return no value. .SH "SEE ALSO" .BR tickit_debug (7), .BR tickit (7) libtickit-0.3.4/man/tickit_get_rootwin.30000644000000000000000000000115413613430267016376 0ustar 00000000000000.TH TICKIT_GET_ROOTWIN 3 .SH NAME tickit_get_rootwin \- obtain the root window from the toplevel instance .SH SYNOPSIS .EX .B #include .sp .BI "TickitWindow *tickit_get_rootwin(Tickit *" t ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_get_rootwin\fP() obtains the root window instance associated with the toplevel instance, creating it first if necessary. Once created, repeated calls to this function will return the same window. .SH "RETURN VALUE" \fBtickit_get_rootwin\fP() returns a window instance pointer. .SH "SEE ALSO" .BR tickit_new_stdio (3), .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_get_term.30000644000000000000000000000112713613430267015644 0ustar 00000000000000.TH TICKIT_GET_TERM 3 .SH NAME tickit_get_term \- obtain the terminal from the toplevel instance .SH SYNOPSIS .EX .B #include .sp .BI "TickitTerm *tickit_get_term(Tickit *" t ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_get_term\fP() obtains the terminal instance associated with the toplevel instance, creating it first if necessary. Once created, repeated calls to this function will return the same terminal. .SH "RETURN VALUE" \fBtickit_get_term\fP() returns a terminal instance pointer. .SH "SEE ALSO" .BR tickit_new_stdio (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_new_stdio.30000644000000000000000000000130513613430267016027 0ustar 00000000000000.TH TICKIT_NEW_STDIO 3 .SH NAME tickit_new_stdio \- create a new toplevel Tickit instance .SH SYNOPSIS .EX .B #include .sp .BI "Tickit *tickit_new_stdio(void);" .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_new_stdio\fP() creates a new toplevel Tickit instance whose terminal is attached to the standard input and output streams. .PP The reference count of a newly-constructed toplevel instance will be one. This can be incremented or decremented using \fBtickit_ref\fP(3) and \fBtickit_unref\fP(3). When its reference count reaches zero it is destroyed. .SH "RETURN VALUE" If successful, \fBtickit_new_stdio\fP() returns a pointer to the new instance. .SH "SEE ALSO" .BR tickit (7) libtickit-0.3.4/man/tickit_pen.70000644000000000000000000001263013613430267014625 0ustar 00000000000000.TH TICKIT_PEN 7 .SH NAME TickitPen \- store a collection of terminal rendering attributes .SH SYNOPSIS .EX .B #include .sp .BI "typedef struct " TickitPen ; .EE .sp .SH DESCRIPTION A \fBTickitPen\fP instance stores a collection of terminal rendering attributes. For each known attribute, a pen instance may store a value for this attribute. A pen instance is used in the \fBtickit_term_chpen\fP(3) and \fBtickit_term_setpen\fP(3) functions. .SH FUNCTIONS A new \fBTickitPen\fP instance is created using the \fBtickit_pen_new\fP(3) function. A pen instance stores a reference count to make it easier for applications to manage the lifetime of pens. A new pen starts with a count of one, and it can be adjusted using \fBtickit_pen_ref\fP(3) and \fBtickit_pen_unref\fP(3). When the count reaches zero the instance is destroyed. .SH ATTRIBUTES The \fBTickitPenAttr\fP enumeration lists the attributes known by a pen. Each attribute has a type, as given by the \fBTickitPenAttrType\fP enumeration. The \fBtickit_pen_attrtype\fP(3) function gives the type of a given attribute. The \fBtickit_pen_attrname\fP(3) function gives the name of a given attribute, which is also given below in parentheses. The \fBtickit_pen_lookup_attr\fP(3) function looks up an attribute constant from a string containing the name. .TP \fBTICKIT_PEN_FG\fP ("fg"), \fBTICKIT_PEN_BG\fP ("bg") foreground and background colours. These are of type \fBTICKIT_PENTYPE_COLOUR\fP. This gives an integer from 0 to 255 to use as the colour index. It may also be set to the value -1, which sets the terminal's default, which may be a colour distinct from any of the others. Some terminals may not support more than 8 or 16 colours, however. When setting a pen colour on a terminal, it may be converted to one that is supported by the terminal \fBtickit_term_setpen\fP(3) or \fBtickit_term_chpen\fP(3) functions. .PP These colour attributes also support a secondary RGB8 (24-bit) specification, which some terminal drivers may prefer over the indexed version. .TP \fBTICKIT_PEN_BOLD\fP ("b"), \fBTICKIT_PEN_ITALIC\fP ("i"), \fBTICKIT_PEN_REVERSE\fP ("rv"), \fBTICKIT_PEN_STRIKE\fP ("strike"), \fBTICKIT_PEN_BLINK\fP ("blink") boolean rendering attributes. These are of type \fBTICKIT_PENTYPE_BOOL\fP. They are either true or false. Most terminals should support at least bold, underline, reverse and blink, though italic and strikethrough are less commonly supported. Applications are advised not to use these two alone to distinguish significant parts of the user interface. .TP \fBTICKIT_PEN_UNDER\fP ("u") underline. This is of type \fBTICKIT_PENTYPE_INT\fP. Its value is one of the values from the \fBTickitPenUnderline\fP enumeration. Most terminals should support the single underline value, however other values are less commonly supported, and may be drawn instead as a single underline. Applications are advised not to make use of underline style alone to distinguish significant parts of the user interface. .IP Valid values are: .RS .TP \fBTICKIT_PEN_UNDER_NONE\fB No underline. .TP \fBTICKIT_PEN_UNDER_SINGLE\fB Single underline. .TP \fBTICKIT_PEN_UNDER_DOUBLE\fB Double underline. .TP \fBTICKIT_PEN_UNDER_WAVY\fB Single wavy underline, sometimes called "undercurl". .RE .IP For backward-compatibility this attribute will also behave like a boolean-typed attribute. When set to any value other than none, it appears true as a boolean. Setting it to true as a booelan will set its value to single. .TP \fBTICKIT_PEN_ALTFONT\fP ("af") alternate font index. This is of type \fBTICKIT_PENTYPE_INT\fP. It is a value 0 to 9, which selects from the terminal's available fonts. Few terminals actually support this in practice. .SH FUNCTIONS The values of attributes are set or queried on a pen instance by using functions depending on the type of the attribute. Boolean attributes use \fBtickit_pen_set_bool_attr\fP(3) and \fBtickit_pen_get_bool_attr\fP(3). Integer attributes use \fBtickit_pen_set_int_attr\fP(3) and \fBtickit_pen_get_int_attr\fP(3). Colour attributes use \fBtickit_pen_set_colour_attr\fP(3), \fBtickit_pen_set_colour_attr_desc\fP(3) and \fBtickit_pen_get_colour_attr\fP(3). The RGB8 secondary field for colours can be set with \fBtickit_pen_set_colour_attr_rgb8\fP(3), and queried with \fBtickit_pen_has_colour_attr_rgb8\fP(3) and \fBtickit_pen_get_colour_attr_rgb8\fP(3). .PP To test if an attribute has a value set, use \fBtickit_pen_has_attr\fP(3), and to remove the attribute entirely use \fBtickit_pen_clear_attr\fP(3). To test if a pen has any attributes set at all, use \fBtickit_pen_is_nonempty\fP(3), and to test if it has any attributes set to a non-default value use \fBtickit_pen_is_nondefault\fP(3). To remove all the attributes use \fBtickit_pen_clear\fP(3). To copy the value of one attribute from a pen into another pen use \fBtickit_pen_copy_attr\fP(3), to copy the entire pen use \fBtickit_pen_copy\fP(3), and to compare two pens for equallity use \fBtickit_pen_equiv_attr\fP(3). .SH EVENTS A pen instance stores a list of event handlers. Each event handler is associated with one event type and stores a function pointer, and an arbitrary pointer containing user data. Event handlers may be installed using \fBtickit_pen_bind_event\fP(3) and removed using \fBtickit_pen_unbind_event_id\fP(3). .PP The event types recognised are: .TP .B TICKIT_PEN_ON_DESTROY The pen instance is being destroyed. .TP .B TICKIT_PEN_ON_CHANGE The stored attributes on the pen have changed. The \fIinfo\fP pointer will be \fBNULL\fP. libtickit-0.3.4/man/tickit_pen_attrname.30000644000000000000000000000142713613430267016516 0ustar 00000000000000.TH TICKIT_PEN_ATTRNAME 3 .SH NAME tickit_pen_attrname \- return the name of a pen attribute .SH SYNOPSIS .EX .B #include .sp .BI "const char * tickit_pen_attrname(TickitPenAttr " attr ); .BI "TickitPenAttr tickit_pen_lookup_attr(const char * " name ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_pen_attrname\fP() returns the name of the given pen attribute. These names are the lowercase strings given in \fBtickit_pen\fP(7). .PP \fBtickit_pen_lookup_attr\fP() returns the attribute constant for a given name. If the name is not recognised then -1 is returned. .SH "RETURN VALUE" \fBtickit_pen_attrname\fP() returns a constant string pointer. \fBtickit_pen_lookup_attr\fP() returns an attribute constant or -1. .SH "SEE ALSO" .BR tickit_pen (7), .BR tickit (7) libtickit-0.3.4/man/tickit_pen_attrtype.30000644000000000000000000000107113613430267016552 0ustar 00000000000000.TH TICKIT_PEN_ATTRTYPE 3 .SH NAME tickit_pen_attrtype \- return the type of a pen attribute .SH SYNOPSIS .EX .B #include .sp .BI "TickitPenAttrType tickit_pen_attrtype(TickitPenAttr " attr ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_pen_attrtype\fP() returns the type of the given pen attribute, as one of the constants \fBTICKIT_PENTYPE_BOOL\fP, \fBTICKIT_PENTYPE_INT\fP or \fBTICKIT_PENTYPE_COLOUR\fP. .SH "RETURN VALUE" \fBtickit_pen_attrtype\fP() returns a pen attribute type value. .SH "SEE ALSO" .BR tickit_pen (7), .BR tickit (7) libtickit-0.3.4/man/tickit_pen_bind_event.30000644000000000000000000000264113613430267017017 0ustar 00000000000000.TH TICKIT_PEN_BIND_EVENT 3 .SH NAME tickit_pen_bind_event, tickit_pen_unbind_event_id \- add or remove event handlers .SH SYNOPSIS .EX .B #include .sp .BI "typedef int " TickitPenEventFn "(TickitPen *" tt ", TickitEventFlags " flags , .BI " void *" info ", void *" user ); .sp .BI "int tickit_pen_bind_event(TickitPen *" tt ", TickitPenEvent " ev , .BI " TickitBindFlags " flags , .BI " TickitPenEventFn *" fn ", void *" user ); .BI "void tickit_pen_unbind_event_id(TickitPen *" tt ", int " id ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_pen_bind_event\fP() adds a new event handler to the list of handlers stored by the pen, and returns an integer to identify this handler. This handler will be invoked for occurances of the event given by the \fIev\fP argument. When invoked, \fIfunc\fP will be passed the pen instance, a flags bitmask, a pointer to an event information structure whose type depends on the event, and the user data pointer it was installed with. .PP \fBtickit_pen_unbind_event_id\fP() removes an event handler previously added, by the identifier returned when it was added, invoking it with the \fBTICKIT_EV_UNBIND\fP flag if it was installed with \fBTICKIT_BIND_UNBIND\fP. .SH "RETURN VALUE" \fBtickit_pen_bind_event\fP() returns an identifier integer. \fBtickit_pen_unbind_event_id\fP() returns no value. .SH "SEE ALSO" .BR tickit_pen_new (3), .BR tickit_pen (7), .BR tickit (7) libtickit-0.3.4/man/tickit_pen_clear.30000644000000000000000000000147513613430267015774 0ustar 00000000000000.TH TICKIT_PEN_CLEAR 3 .SH NAME tickit_pen_clear, tickit_pen_clear_attr \- remove pen attributes .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_pen_clear(TickitPen *" pen ); .BI "void tickit_pen_clear_attr(TickitPen *" pen ", TickitPenAttr " attr ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_pen_clear\fP() removes all the attributes from the pen instance. .PP \fBtickit_pen_clear_attr\fP() removes the given attribute from the pen instance. After calling this function, \fBtickit_pen_has_attr\fP(3) will return false for this attribute. .SH "RETURN VALUE" These functions return no value. .SH "SEE ALSO" .BR tickit_pen_new (3), .BR tickit_pen_has_attr (3), .BR tickit_pen_set_bool_attr (3), .BR tickit_pen_set_int_attr (3), .BR tickit_pen_set_colour_attr (3), .BR tickit_pen (7), .BR tickit (7) libtickit-0.3.4/man/tickit_pen_copy.30000644000000000000000000000245413613430267015656 0ustar 00000000000000.TH TICKIT_PEN_COPY 3 .SH NAME tickit_pen_copy, tickit_pen_copy_attr \- copy attributes from one pen to another .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_pen_copy(TickitPen *" dst ", TickitPen *" src , .BI " bool " overwrite ); .BI "void tickit_pen_copy_attr(TickitPen *" dst ", TickitPen *" src , .BI " TickitPenAttr " attr ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_pen_copy\fP() copies attributes that are defined on \fIsrc\fP into \fIdst\fP. If an attribute is already defined on \fIdst\fP then it will only be changed if \fIoverwrite\fP is true; otherwise the existing value will be left alone. It will invoke the \fBTICKIT_EV_CHANGE\fP event on the \fIdst\fP pen instance if the value of at least one attribute has actually changed. .PP \fBtickit_pen_copy_attr\fP() copies the value of a single attribute from \fIsrc\fP into \fIdst\fP, or clears it in \fIdst\fP if it is not present in \fIsrc\fP. It will invoke the \fBTICKIT_EV_CHANGE\fP event on the \fIdst\fP pen instance. .SH "RETURN VALUE" \fBtickit_pen_copy\fP() and \fBtickit_pen_copy_attr\fP() return no value. .SH "SEE ALSO" .BR tickit_pen_new (3), .BR tickit_pen_set_bool_attr (3), .BR tickit_pen_set_int_attr (3), .BR tickit_pen_set_colour_attr (3), .BR tickit_pen_clear (3), .BR tickit_pen (7), .BR tickit (7) libtickit-0.3.4/man/tickit_pen_equiv_attr.30000644000000000000000000000175113613430267017066 0ustar 00000000000000.TH TICKIT_PEN_EQUIV_ATTR 3 .SH NAME tickit_pen_equiv_attr \- test if two pens have an equivalent attribute value .SH SYNOPSIS .EX .B #include .sp .BI "bool tickit_pen_equiv_attr(TickitPen *" a ", TickitPen *" b , .BI " TickitPenAttr " attr ); .BI "bool tickit_pen_equiv(TickitPen *" a ", TickitPen *" b ) .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_pen_equiv_attr\fP() tests whether two given pen instances have equivalent values for given attribute. It returns true if neither pen has the attribute defined, or if both have it defined to the same value. .PP \fBtickit_pen_equiv\fP() tests whether two given pen instances have equivalent values for all attributes. .SH "RETURN VALUE" \fBtickit_pen_equiv_attr\fP() and \fBtickit_pen_equiv\fP() return a boolean. .SH "SEE ALSO" .BR tickit_pen_new (3), .BR tickit_pen_set_bool_attr (3), .BR tickit_pen_set_int_attr (3), .BR tickit_pen_set_colour_attr (3), .BR tickit_pen_clear_attr (3), .BR tickit_pen (7), .BR tickit (7) libtickit-0.3.4/man/tickit_pen_get_bool_attr.30000644000000000000000000000201513613430267017521 0ustar 00000000000000.TH TICKIT_PEN_GET_BOOL_ATTR 3 .SH NAME tickit_pen_get_bool_attr, tickit_pen_set_bool_attr \- manipulate a boolean pen attribute .SH SYNOPSIS .EX .B #include .sp .BI "bool tickit_pen_get_bool_attr(TickitPen *" pen ", TickitPenAttr " attr ); .BI "void tickit_pen_set_bool_attr(TickitPen *" pen ", TickitPenAttr " attr ", bool " val ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_pen_get_bool_attr\fP() returns the current value of the given boolean attribute on the pen, or false if that attribute is not defined. .PP \fBtickit_pen_set_bool_attr\fP() provides a new value for the given boolean attribute on the pen. It will invoke the \fBTICKIT_EV_CHANGE\fP event on the pen instance. .SH "RETURN VALUE" \fBtickit_pen_get_bool_attr\fP() returns a boolean. \fBtickit_pen_set_bool_attr\fP() returns no value. .SH "SEE ALSO" .BR tickit_pen_new (3), .BR tickit_pen_has_attr (3), .BR tickit_pen_set_int_attr (3), .BR tickit_pen_set_colour_attr (3), .BR tickit_pen_clear_attr (3), .BR tickit_pen (7), .BR tickit (7) libtickit-0.3.4/man/tickit_pen_get_colour_attr.30000644000000000000000000000375313613430267020103 0ustar 00000000000000.TH TICKIT_PEN_GET_COLOUR_ATTR 3 .SH NAME tickit_pen_get_colour_attr, tickit_pen_set_colour_attr, tickit_pen_set_colour_attr_desc \- manipulate a colour pen attribute .SH SYNOPSIS .EX .B #include .sp .BI "int tickit_pen_get_colour_attr(TickitPen *" pen ", TickitPenAttr " attr ); .BI "void tickit_pen_set_colour_attr(TickitPen *" pen ", TickitPenAttr " attr ", int " val ); .BI "bool tickit_pen_set_colour_attr_desc(TickitPen *" pen ", TickitPenAttr " attr , .BI " const char *" desc ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_pen_get_colour_attr\fP() returns the current value of the given colour attribute on the pen as a palette index, or -1 if that attribute is not defined. .PP \fBtickit_pen_set_colour_attr\fP() provides a new value for the given colour attribute on the pen as a palette index. If the pen previously stored a secondary RGB8 value for this attribute it will be cleared. It will invoke the \fBTICKIT_EV_CHANGE\fP event on the pen instance. .PP \fBtickit_pen_set_colour_attr_desc\fP() provides a new value for the given colour attribute on the pen as a textual description string, converting it into a colour index. It will invoke the \fBTICKIT_EV_CHANGE\fP event on the pen instance. It recognises decimal integers and the names of the first eight VGA colours (black, red, green, yellow, blue, magenta, cyan and white, respectively). It also recognises the prefix of "hi-" before one of the eight VGA colour names, to indicate the high-brightness versions at colour indexes 8 to 15. If it recognises the given string it returns a true value, or false if not. .SH "RETURN VALUE" \fBtickit_pen_get_colour_attr\fP() returns an integer value. \fBtickit_pen_set_colour_attr\fP() returns no value. \fBtickit_pen_set_colour_attr_desc\fP() returns a boolean indicating success. .SH "SEE ALSO" .BR tickit_pen_new (3), .BR tickit_pen_has_attr (3), .BR tickit_pen_set_bool_attr (3), .BR tickit_pen_set_int_attr (3), .BR tickit_pen_clear_attr (3), .BR tickit_pen (7), .BR tickit (7) libtickit-0.3.4/man/tickit_pen_get_colour_attr_rgb8.30000644000000000000000000000465113613430267021023 0ustar 00000000000000.TH TICKIT_PEN_GET_COLOUR_ATTR_RGB8 3 .SH NAME tickit_pen_get_colour_attr_rgb8, tickit_pen_set_colour_attr_rgb8, tickit_pen_has_attr_rgb8 \- manipulate a secondary RGB8 colour pen colour attribute .SH SYNOPSIS .EX .B #include .sp .B "typedef struct {" .BI " uint8_t " r ; .BI " uint8_t " g ; .BI " uint8_t " b ; .BI "} " TickitPenRGB8 ; .sp .BI "TickitPenRGB8 tickit_pen_get_colour_attr_rgb8(const TickitPen *" pen , .BI " TickitPenAttr " attr ); .BI "void tickit_pen_set_colour_attr_rgb8(TickitPen *" pen , .BI " TickitPenAttr " attr ", TickitPenRGB8 " value ); .sp .BI "bool tickit_pen_has_attr_rgb8(const TickitPen *" pen , .BI " TickitPenAttr " attr ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_pen_get_colour_attr_rgb8\fP() returns the current secondary RGB8 value of the given colour attribute. If the attribute is not defined or does not have a secondary RGB8 value, the return value is not specified. .PP \fBtickit_pen_set_colour_attr_rgb8\fP() provides a new value for the secondary RGB8 value of the given colour attribute on the pen. This will only be stored if the pen already has a (primary) index value set for the corresponding attribute. It will invoke the \fBTICKIT_EV_CHANGE\fP event on the pen instance if it successfully stores the value. .PP \fBtickit_pen_has_colour_attr_rgb8\fP() returns true if the pen instance currently stores a secondary RGB8 value for the given colour attribute. .SH NOTES A pen instance will only store a secondary RGB8 value if a primary index value is already set. Additionally, calling \fBtickit_pen_set_colour_attr\fP(3) will clear an associated secondary RGB8 value. This design aims to encourage that any use of an RGB8 colour also has an index as well, to support the majority of terminals which do not in fact understand RGB8 values. It is recommended that applications wishing to make use of these values should set both at the same time, in sequence. .sp .EX .in tickit_pen_set_colour_attr(pen, index); tickit_pen_set_colour_attr_rgb8(pen, (TickitPenRGB8){.r = red, .g = green, .b = blue}); .EE .SH "RETURN VALUE" \fBtickit_pen_get_colour_attr_rgb8\fP() returns a three-field colour value structure. \fBtickit_pen_set_colour_attr_rgb8\fP() returns no value. \fBtickit_pen_has_colour_attr_rgb8\fP() returns a boolean. .SH "SEE ALSO" .BR tickit_pen_new (3), .BR tickit_pen_set_colour_attr (3), .BR tickit_pen_clear_attr (3), .BR tickit_pen (7), .BR tickit (7) libtickit-0.3.4/man/tickit_pen_get_int_attr.30000644000000000000000000000200713613430267017361 0ustar 00000000000000.TH TICKIT_PEN_GET_INT_ATTR 3 .SH NAME tickit_pen_get_int_attr, tickit_pen_set_int_attr \- manipulate an integer pen attribute .SH SYNOPSIS .EX .B #include .sp .BI "int tickit_pen_get_int_attr(TickitPen *" pen ", TickitPenAttr " attr ); .BI "void tickit_pen_set_int_attr(TickitPen *" pen ", TickitPenAttr " attr ", int " val ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_pen_get_int_attr\fP() returns the current value of the given integer attribute on the pen, or 0 if that attribute is not defined. .PP \fBtickit_pen_set_int_attr\fP() provides a new value for the given integer attribute on the pen. It will invoke the \fBTICKIT_EV_CHANGE\fP event on the pen instance. .SH "RETURN VALUE" \fBtickit_pen_get_int_attr\fP() returns an integer value. \fBtickit_pen_set_int_attr\fP() returns no value. .SH "SEE ALSO" .BR tickit_pen_new (3), .BR tickit_pen_has_attr (3), .BR tickit_pen_set_bool_attr (3), .BR tickit_pen_set_colour_attr (3), .BR tickit_pen_clear_attr (3), .BR tickit_pen (7), .BR tickit (7) libtickit-0.3.4/man/tickit_pen_has_attr.30000644000000000000000000000234713613430267016512 0ustar 00000000000000.TH TICKIT_PEN_HAS_ATTR 3 .SH NAME tickit_pen_has_attr \- test if a pen has a given attribute .SH SYNOPSIS .EX .B #include .sp .BI "bool tickit_pen_has_attr(TickitPen *" pen ", TickitPenAttr " attr ); .BI "bool tickit_pen_nondefault_attr(TickitPen *" pen ", TickitPenAttr " attr ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_pen_has_attr\fP() tests whether a pen instance has a given attribute defined on it. It returns true if the corresponding \fBtickit_pen_set_*_attr\fP() function has been called since construction, or the last time it was cleared. This is true even if the attribute has been set to its default value. .PP \fBtickit_pen_nondefault_attr\fP() tests whether a pen instance has a given attribute defined with a non-default value. It returns true if the attribute is set to a value that is not the default, or false if the attribute is absent, or set to the default value for that attribute type. .SH "RETURN VALUE" \fBtickit_pen_has_attr\fP() and \fBtickit_pen_nondefault_attr\fP() return a boolean. .SH "SEE ALSO" .BR tickit_pen_new (3), .BR tickit_pen_set_bool_attr (3), .BR tickit_pen_set_int_attr (3), .BR tickit_pen_set_colour_attr (3), .BR tickit_pen_clear_attr (3), .BR tickit_pen (7), .BR tickit (7) libtickit-0.3.4/man/tickit_pen_is_nonempty.30000644000000000000000000000202013613430267017235 0ustar 00000000000000.TH TICKIT_PEN_IS_NONEMPTY 3 .SH NAME tickit_pen_is_nonempty, tickit_pen_is_nondefault \- test if a pen has attributes defined .SH SYNOPSIS .EX .B #include .sp .BI "bool tickit_pen_is_nonempty(TickitPen *" pen ); .BI "bool tickit_pen_is_nondefault(TickitPen *" pen ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_pen_is_nonempty\fP() tests whether a pen instance has any attribute defined on it. It returns true even if those attributes take default values. .PP \fBtickit_pen_is_nondefault\fP() tests whether a pen instance has any attribute with non-default values defined on it. It false if the pen has no defined attributes, or all of the defined attributes take only the default value for their type. .SH "RETURN VALUE" \fBtickit_pen_is_nonempty\fP() and \fBtickit_pen_is_nondefault\fP() return a boolean. .SH "SEE ALSO" .BR tickit_pen_new (3), .BR tickit_pen_set_bool_attr (3), .BR tickit_pen_set_int_attr (3), .BR tickit_pen_set_colour_attr (3), .BR tickit_pen_clear (3), .BR tickit_pen (7), .BR tickit (7) libtickit-0.3.4/man/tickit_pen_new.30000644000000000000000000000260213613430267015470 0ustar 00000000000000.TH TICKIT_PEN_NEW 3 .SH NAME tickit_pen_new \- create a new pen instance .SH SYNOPSIS .EX .B #include .sp .BI "TickitPen *tickit_pen_new(void);" .BI "TickitPen *tickit_pen_new_attrs(...);" .BI "TickitPen *tickit_pen_clone(const TickitPen *" orig ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_pen_new\fP() creates a new \fBTickitPen\fP instance. It initially has no attributes set. .PP \fBtickit_pen_new_attrs\fP() creates a new \fBTickitPen\fP instance and populates it with a given list of attribute values. The attributes are given as pairs; first a \fBTickitPenAttr\fP then an integer giving its value, terminated by a final 0 or -1. .PP \fBtickit_pen_clone\fP() creates a new \fBTickitPen\fP instance. It initially has the same attributes values set as the one given by \fIorig\fP. .PP The reference count of a newly-constructed pen instance will be one. This can be incremented or decremented using \fBtickit_pen_ref\fP(3) and \fBtickit_pen_unref\fP(3). When its reference count reaches zero it is destroyed. .SH "RETURN VALUE" If successful, \fBtickit_pen_new\fP(), \fBtickit_pen_new_attrs\fP() and \fBtickit_pen_clone\fP() return a pointer to the new instance. On failure, \fBNULL\fP is returned with \fIerrno\fP set to indicate the failure. .SH "SEE ALSO" .BR tickit_pen_ref (3), .BR tickit_pen_unref (3), .BR tickit_pen_bind_event (3), .BR tickit_pen (7), .BR tickit (7) libtickit-0.3.4/man/tickit_pen_ref.30000644000000000000000000000170413613430267015455 0ustar 00000000000000.TH TICKIT_PEN_REF 3 .SH NAME tickit_pen_ref, tickit_pen_unref \- adjust the refcount of a pen .SH SYNOPSIS .EX .B #include .sp .BI "TickitPen *tickit_pen_ref(TickitPen *" pen ); .BI "void tickit_pen_unref(TickitPen *" pen ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_pen_ref\fP() increments the stored refcount of the given pen instance by one. It returns the pointer argument itself, so it is useful for chaining. .PP \fBtickit_pen_unref\fP() decrements the stored refcount of the given pen instance by one. If the refcount drops to zero, the instance is destroyed. This will release any resources controlled by it and unbind all the bound event handlers, causing handlers to be invoked with the \fBTICKIT_EV_DESTROY\fP flag if appropriate. .SH "RETURN VALUE" \fBtickit_pen_ref\fP() returns a pen instance pointer. \fBtickit_pen_unref\fP() returns no value. .SH "SEE ALSO" .BR tickit_pen_new (3), .BR tickit_pen (7), .BR tickit (7) libtickit-0.3.4/man/tickit_rect.70000644000000000000000000000206413613430267015000 0ustar 00000000000000.TH TICKIT_RECT 7 .SH NAME TickitRect \- utility structure representing a rectangular region .SH SYNOPSIS .EX .B #include .sp .BI "typedef struct {" .BI " int " top ; .BI " int " left ; .BI " int " lines ; .BI " int " cols ; .BI "} " TickitRect ; .EE .sp .SH DESCRIPTION A \fBTickitRect\fP structure represents a single non-empty rectangular region of the screen. It stores the position of the top left corner of the region, and its size in lines and columns. .SH FUNCTIONS A new \fBTickitRect\fP structure can be initialised using the \fBtickit_rect_init_sized\fP(3) and \fBtickit_rect_init_bounded\fP(3) functions. Its bottom right corner can be obtained using \fBtickit_rect_bottom\fP(3) and \fBtickit_rect_right\fP(3). Interactions between rectangles can be tested using the predicate functions \fBtickit_rect_contains\fP(3) and \fBtickit_rect_intersects\fP(3). New rectangles can be created from existing ones using \fBtickit_rect_intersect\fP(3) to form the intersection, \fBtickit_rect_add\fP(3) to form the union sum, and \fBtickit_rect_subtract\fP(3). libtickit-0.3.4/man/tickit_rect_add.30000644000000000000000000000236213613430267015605 0ustar 00000000000000.TH TICKIT_RECT_ADD 3 .SH NAME tickit_rect_add \- obtain the union sum of two rectangles .SH SYNOPSIS .EX .B #include .sp .BI "int tickit_rect_add(TickitRect " dst [3], .BI " const TickitRect *" a ", const TickitRect *" b ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_rect_add\fP() initialises the rectangle structures given by \fIdst\fP (which must be an array capable of containing at least three rectangle structures) with a set of non-overlapping rectangles covering the same area as the two given by \fIa\fP and \fIb\fP. Depending on the geometry of the given rectangles it may create one, two, or three resultant rectangles. It returns the number of rectangular regions initialised into the result array. .PP If the original rectangles do not touch then the result will contain just those two. If they touch then the result will contain the one to three non-overlapping horizontal stripes that cover the same area, in order of increasing line number. .SH "RETURN VALUE" \fBtickit_rect_add\fP() returns the number of rectangles written to \fIdst\fP. .SH "SEE ALSO" .BR tickit_rect_init_sized (3), .BR tickit_rect_init_bounded (3), .BR tickit_rect_intersect (3), .BR tickit_rect_subtract (3), .BR tickit_rect (7), .BR tickit (7) libtickit-0.3.4/man/tickit_rect_bottom.30000644000000000000000000000156313613430267016363 0ustar 00000000000000.TH TICKIT_RECT_BOTTOM 3 .SH NAME tickit_rect_bottom, tickit_rect_right \- return the bottom right corner of a rectangle .SH SYNOPSIS .EX .B #include .sp .BI "int tickit_rect_bottom(const TickitRect *" rect ); .BI "int tickit_rect_right(const TickitRect *" rect ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_rect_bottom\fP() returns the lower extent of the rectangle; returning the first line past the bottom. .PP \fBtickit_rect_right\fP() returns the righthand extent of the rectangle; returning the first column past the righthand edge. .PP These functions may be \f(CWstatic inline\fP. .SH "RETURN VALUE" \fBtickit_rect_bottom\fP() returns a line number as an integer. \fBtickit_rect_right\fP() returns a column number as an integer. .SH "SEE ALSO" .BR tickit_rect_init_sized (3), .BR tickit_rect_init_bounded (3), .BR tickit_rect (7), .BR tickit (7) libtickit-0.3.4/man/tickit_rect_contains.30000644000000000000000000000120613613430267016667 0ustar 00000000000000.TH TICKIT_RECT_CONTAINS 3 .SH NAME tickit_rect_contains \- test if one rectangle entirely contains another .SH SYNOPSIS .EX .B #include .sp .BI "bool tickit_rect_contains(const TickitRect *" large ", const TickitRect *" small ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_rect_contains\fP() tests whether the rectangle given by \fIlarge\fP entirely contains the rectangle given by \fIsmall\fP. .SH "RETURN VALUE" \fBtickit_rect_contains\fP() returns a boolean value. .SH "SEE ALSO" .BR tickit_rect_init_sized (3), .BR tickit_rect_init_bounded (3), .BR tickit_rect_intersects (3), .BR tickit_rect (7), .BR tickit (7) libtickit-0.3.4/man/tickit_rect_init_sized.30000644000000000000000000000172513613430267017220 0ustar 00000000000000.TH TICKIT_RECT_INIT_SIZED 3 .SH NAME tickit_rect_init_sized, tickit_rect_init_bounded \- initialise a rectangle struct .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_rect_init_sized(TickitRect *" rect ", int " top ", int " left , .BI " int " lines ", int " cols ); .BI "void tickit_rect_init_bounded(TickitRect *" rect ", int " top ", int " left , .BI " int " bottom ", int " right ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_rect_init_sized\fP() initialises a \fBTickitRect\fP structure to contain the given top left corner and size. It is equivalent to just initialising the four fields individually, but is included in the API for completeness. .PP \fBtickit_rect_init_bounded\fP() initialises a \fBTickitRect\fP structure to contain the given top left and bottom right bounds. .SH "RETURN VALUE" Neither function returns a value. .SH "SEE ALSO" .BR tickit_rect_bottom (3), .BR tickit_rect_right (3), .BR tickit_rect (7), .BR tickit (7) libtickit-0.3.4/man/tickit_rect_intersect.30000644000000000000000000000236413613430267017057 0ustar 00000000000000.TH TICKIT_RECT_INTERSECT 3 .SH NAME tickit_rect_intersect \- obtain the intersection of two rectangles .SH SYNOPSIS .EX .B #include .sp .BI "bool tickit_rect_intersect(TickitRect *" dst , .BI " const TickitRect *" a ", const TickitRect *" b ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_rect_intersect\fP() tests if the two rectangles given by \fIa\fP and \fIb\fP intersect and if so, initialises the rectangle structure given by \fIdst\fP to contain it. It returns true if it did this. If the two given rectangles do not intersect then it returns false and does not modify the structure given by \fIdst\fP. .PP \fIdst\fP may be equal to either \fIa\fP or \fIb\fP, or it may refer to a distinct rectangle structure. The two source rectangles are read before the result is written. .PP To simply test if two given rectangles intersect without obtaining the actual intersection region, use \fBtickit_rect_intersects\fP(3). .SH "RETURN VALUE" \fBtickit_rect_intersect\fP() returns true if the rectangles intersect, and false if not. .SH "SEE ALSO" .BR tickit_rect_init_sized (3), .BR tickit_rect_init_bounded (3), .BR tickit_rect_intersects (3), .BR tickit_rect_add (3), .BR tickit_rect_subtract (3), .BR tickit_rect (7), .BR tickit (7) libtickit-0.3.4/man/tickit_rect_intersects.30000644000000000000000000000150213613430267017233 0ustar 00000000000000.TH TICKIT_RECT_INTERSECTS 3 .SH NAME tickit_rect_intersects \- test if two rectangles intersect .SH SYNOPSIS .EX .B #include .sp .BI "bool tickit_rect_intersects(const TickitRect *" a ", const TickitRect *" b ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_rect_intersects\fP() tests whether the two given rectangles intersect at all. It returns true if there is a non-empty rectangular region in common covered by both rectangles. \fBtickit_rect_intersect\fP(3) can be used to obtain this intersection as well as performing the intersection test itself. .SH "RETURN VALUE" \fBtickit_rect_contains\fP() returns a boolean value. .SH "SEE ALSO" .BR tickit_rect_init_sized (3), .BR tickit_rect_init_bounded (3), .BR tickit_rect_contains (3), .BR tickit_rect_intersect (3), .BR tickit_rect (7), .BR tickit (7) libtickit-0.3.4/man/tickit_rect_subtract.30000644000000000000000000000245013613430267016702 0ustar 00000000000000.TH TICKIT_RECT_SUBTRACT 3 .SH NAME tickit_rect_subtract \- obtain the difference of two rectangles .SH SYNOPSIS .EX .B #include .sp .BI "int tickit_rect_subtract(TickitRect " dst [4], .BI " const TickitRect *" orig ", const TickitRect *" hole ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_rect_subtract\fP() initialises the rectangle structures given by \fIdst\fP (which must be an array capable of containing at least four rectangle structures) with a set of non-overlapping rectangles covering the same area as that given by \fIorig\fP without covering \fIhole\fP. Depending on the geometry of the given rectangles it will create from zero to four resultant rectangles. It returns the number of rectangular regions initialised into the result array. .PP If \fIhole\fP completely covers \fIorig\fP then the result will contain no rectangles. If they do not intersect then just \fIorig\fP will be returned. Otherwise, the result will contain non-overlapping horizontal stripes of \fIorig\fP without \fIhole\fP. .SH "RETURN VALUE" \fBtickit_rect_subtract\fP() returns the number of rectangles written to \fIdst\fP. .SH "SEE ALSO" .BR tickit_rect_init_sized (3), .BR tickit_rect_init_bounded (3), .BR tickit_rect_intersect (3), .BR tickit_rect_add (3), .BR tickit_rect (7), .BR tickit (7) libtickit-0.3.4/man/tickit_rect_translate.30000644000000000000000000000106413613430267017050 0ustar 00000000000000.TH TICKIT_RECT_TRANSLATE 3 .SH NAME tickit_rect_translate \- move the area of a rectangle .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_rect_translate(TickitRect *" rect ", int " downward ", int " rightward ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_rect_translate\fP() adds to both the \fItop\fP and \fIleft\fP members of the given rectangle. .SH "RETURN VALUE" \fBtickit_rect_translate\fP() returns nothing. .SH "SEE ALSO" .BR tickit_rect_init_sized (3), .BR tickit_rect_init_bounded (3), .BR tickit_rect (7), .BR tickit (7) libtickit-0.3.4/man/tickit_rectset.70000644000000000000000000000230513613430267015512 0ustar 00000000000000.TH TICKIT_RECTSET 7 .SH NAME TickitRectSet \- store a collection of rectangular areas .SH SYNOPSIS .EX .B #include .sp .BI "typedef struct " TickitRectSet ; .EE .sp .SH DESCRIPTION A \fBTickitRectSet\fP instance stores a collection of rectangular areas as non-overlapping regions. Mutation operations allow areas to be added or subtracted. Query operations allow testing for area containment, intersection, or iterating the stored regions. .SH FUNCTIONS A new \fBTickitRectSet\fP instance is created using the \fBtickit_rectset_new\fP(3) function, and destroyed using \fBtickit_rectset_destroy\fP(3). .PP Rectangular areas can be added using \fBtickit_rectset_add\fP(3) and subtracted using \fBtickit_rectset_subtract\fP(3). The \fBTickitRectSet\fP can be emptied of regions entirely by using \fBtickit_rectset_clear\fP(3). The entire set of regions can be uniformly moved using \fBtickit_rectset_translate\fP(3). .PP The stored regions can be queried by using \fBtickit_rectset_rects\fP(3) and \fBtickit_rectset_get_rects\fP(3). An area can be tested to see if it is entirely contained in the set using \fBtickit_rectset_contains\fP(3), or that it intersects at all using \fBtickit_rectset_intersects\fP(3). libtickit-0.3.4/man/tickit_rectset_add.30000644000000000000000000000144613613430267016323 0ustar 00000000000000.TH TICKIT_RECTSET_ADD 3 .SH NAME tickit_rectset_add \- add an area to a rectangle set .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_rectset_add(TickitRectSet *" trs ", const TickitRect *" rect ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_rectset_add\fP() ensures that the regions stored by the rectangle set includes the area given by \fIrect\fP. Since the rectangle set stores a set of non-overlapping regions, it may have to split the newly-added area into smaller pieces, to ensure the regions do not overlap. Since it merges neigbours where possible it can also result in fewer regions being stored. .SH "RETURN VALUE" \fBtickit_rectset_add\fP() returns no value. .SH "SEE ALSO" .BR tickit_rectset_new (3), .BR tickit_rectset (7), .BR tickit_rect (7), .BR tickit (7) libtickit-0.3.4/man/tickit_rectset_clear.30000644000000000000000000000073413613430267016660 0ustar 00000000000000.TH TICKIT_RECTSET_CLEAR 3 .SH NAME tickit_rectset_clear \- remove all regions from a rectangle set .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_rectset_clear(TickitRectSet *" trs ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_rectset_clear\fP() removes all regions stored by the rectangle set. .SH "RETURN VALUE" \fBtickit_rectset_clear\fP() returns no value. .SH "SEE ALSO" .BR tickit_rectset_new (3), .BR tickit_rectset (7), .BR tickit (7) libtickit-0.3.4/man/tickit_rectset_contains.30000644000000000000000000000134313613430267017405 0ustar 00000000000000.TH TICKIT_RECTSET_CONTAINS 3 .SH NAME tickit_rectset_contains \- test if an area is contained by a rectangle set .SH SYNOPSIS .EX .B #include .sp .BI "bool tickit_rectset_contains(const TickitRectSet *" trs ", const TickitRect *" rect ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_rectset_contains\fP() tests whether the area given by \fIrect\fP is entirely contained by one or more regions in the rectangle set. .SH "RETURN VALUE" \fBtickit_rectset_contains\fP() returns a true value if the given area is entirely covered by one or more regions, or false if at least some portion of it remains uncovered. .SH "SEE ALSO" .BR tickit_rectset_new (3), .BR tickit_rectset (7), .BR tickit_rect (7), .BR tickit (7) libtickit-0.3.4/man/tickit_rectset_intersects.30000644000000000000000000000132313613430267017750 0ustar 00000000000000.TH TICKIT_RECTSET_INTERSECTS 3 .SH NAME tickit_rectset_intersects \- test if a region intersects with a rectangle set .SH SYNOPSIS .EX .B #include .sp .BI "bool tickit_rectset_intersects(const TickitRectSet *" trs ", const TickitRect *" rect ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_rectset_intersects\fP() tests whether the area given by \fIrect\fP intersects with one or more regions in the rectangle set. .SH "RETURN VALUE" \fBtickit_rectset_intersects\fP() returns a true value if the the given area intersects with one or more regions, or false if it does not intersect with any. .SH "SEE ALSO" .BR tickit_rectset_new (3), .BR tickit_rectset (7), .BR tickit_rect (7), .BR tickit (7) libtickit-0.3.4/man/tickit_rectset_new.30000644000000000000000000000167113613430267016364 0ustar 00000000000000.TH TICKIT_RECTSET_NEW 3 .SH NAME tickit_rectset_new, tickit_rectset_destroy \- create or destroy a rectangle set .SH SYNOPSIS .EX .B #include .sp .BI "TickitRectSet *tickit_rectset_new(void);" .BI "void tickit_rectset_destroy(TickitRectSet *" trs ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_rectset_new\fP() creates a new \fBTickitRectSet\fP instance. It will be initially empty, containing no regions. .PP \fBtickit_rectset_destroy\fP() destroys the given instances and releases any resources controlled by it. .SH "RETURN VALUE" If successful, \fBtickit_rectset_new\fP() returns a pointer to the new instance. On failure, \fBNULL\fP is returned with \fIerrno\fP set to indicate the failure. \fBtickit_rectset_destroy\fP() returns no value. .SH "SEE ALSO" .BR tickit_rectset_add (3), .BR tickit_rectset_subtract (3), .BR tickit_rectset_rects (3), .BR tickit_rectset_get_rects (3), .BR tickit_rectset (7), .BR tickit (7) libtickit-0.3.4/man/tickit_rectset_rects.30000644000000000000000000000252013613430267016705 0ustar 00000000000000.TH TICKIT_RECTSET_RECTS 3 .SH NAME tickit_rectset_rects, tickit_rectset_get_rects \- obtain the regions from a rectangle set .SH SYNOPSIS .EX .B #include .sp .BI "size_t tickit_rectset_rects(const TickitRectSet *" trs ); .BI "size_t tickit_rectset_get_rect(const TickitRectSet *" trs , .BI " size_t " i ", TickitRect *" rect ); .BI "size_t tickit_rectset_get_rects(const TickitRectSet *" trs , .BI " TickitRect " rects "[], size_t " n ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_rectset_rects\fP() returns the number of rectangular regions stored by the rectangle set. .PP \fBtickit_rectset_get_rect\fP() copies the region representing the rectangle at index \fIi\fP into the variable pointed to by \fIrect\fP, and returns 1. If \fIi\fP is an invalid index for this set then 0 is returned instead. .PP \fBtickit_rectset_get_rects\fP() copies at most \fIn\fP regions into the array given by \fIrects\fP, and returns the number of regions it copied (which may be fewer than the total stored, if the array was of insufficient size). .SH "RETURN VALUE" \fBtickit_rectset_rects\fP() returns an integer giving the number of stored regions. \fBtickit_rectset_get_rect\fP() and \fBtickit_rectset_get_rects\fP() return the number of regions copied. .SH "SEE ALSO" .BR tickit_rectset_new (3), .BR tickit_rectset (7), .BR tickit (7) libtickit-0.3.4/man/tickit_rectset_subtract.30000644000000000000000000000133113613430267017413 0ustar 00000000000000.TH TICKIT_RECTSET_SUBTRACT 3 .SH NAME tickit_rectset_subtract \- subtract a region from a rectangle set .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_rectset_subtract(TickitRectSet *" trs ", const TickitRect *" rect ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_rectset_subtract\fP() ensures that the regions stored by the rectangle set do not include the area given by \fIrect\fP. To do this, it may have to split one or more of the existing regions into smaller fragments, and so may result in more regions being stored. .SH "RETURN VALUE" \fBtickit_rectset_subtract\fP() returns no value. .SH "SEE ALSO" .BR tickit_rectset_new (3), .BR tickit_rectset (7), .BR tickit_rect (7), .BR tickit (7) libtickit-0.3.4/man/tickit_rectset_translate.30000644000000000000000000000132013613430267017557 0ustar 00000000000000.TH TICKIT_RECTSET_TRANSLATE 3 .SH NAME tickit_rectset_translate \- move all the rectangles in a rectangle set .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_rectset_translate(TickitRectSet *" trs ", int " downward ", int " rightward ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_rectset_translate\fP() moves every rectangle in the rectangle set by the given offset. The number of rectangles in the set is not affected by this operation, and since the set has no concept of boundaries, no clipping will take place. .SH "RETURN VALUE" \fBtickit_rectset_translate\fP() returns no value. .SH "SEE ALSO" .BR tickit_rectset_new (3), .BR tickit_rectset (7), .BR tickit_rect (7), .BR tickit (7) libtickit-0.3.4/man/tickit_ref.30000644000000000000000000000204413613430267014611 0ustar 00000000000000.TH TICKIT_REF 3 .SH NAME tickit_ref, tickit_unref \- adjust the refcount of the toplevel instance .SH SYNOPSIS .EX .B #include .sp .BI "Tickit *tickit_ref(Tickit *" t ); .BI "void tickit_unref(Tickit *" t ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_ref\fP() increments the stored refcount of the given toplevel instance by one. It returns the pointer argument itself, so it is useful for chaining. .PP \fBtickit_unref\fP() decrements the stored refcount of the given toplevel instance by one. If the refcount drops to zero, the instance is destroyed. This will release any resources controlled by it and unbind all pending timer and later callbacks, causing them to be invoked with the \fBTICKIT_EV_DESTROY\fP flag if appropriate. This will also decrement refcount on the terminal and root window instances, which will likely cause them to be destroyed too. .SH "RETURN VALUE" \fBtickit_ref\fP() returns a toplevel instance pointer. \fBtickit_unref\fP() returns no value. .SH "SEE ALSO" .BR tickit_new_stdio (3), .BR tickit (7) libtickit-0.3.4/man/tickit_renderbuffer.70000644000000000000000000002135213613430267016515 0ustar 00000000000000.TH TICKIT_RENDERBUFFER 7 .SH NAME TickitRenderBuffer \- store display content to be drawn to the terminal .SH SYNOPSIS .EX .B #include .sp .BI "typedef struct " TickitRenderBuffer ; .EE .sp .SH DESCRIPTION A \fBTickitRenderBuffer\fP instance stores content waiting to be drawn to the terminal. It provides an efficient place to build the eventual display, by applying drawing operations to it that can alter and overwrite the pending content, before eventually flushing it directly to the terminal in an efficient transfer of state. The buffer stores plain text content along with rendering attributes, and also stores line drawing information, allowing line segments to be merged correctly and rendered using Unicode characters. .PP The primary purpose for the render buffer is the storage of pending content to be displayed. The buffer consists of a grid of cells of the given size. Each cell has a state; neighbouring cells in the same state constitute a region. Each region is either in a skip state (where it will not affect the terminal display when it is flushed), or has either textual content or an instruction to erase the display. In both of these cases, the region has an associated \fBTickitPen\fP instance to give its display attributes. Text regions can be given either by .SM UTF-8 strings, individual Unicode codepoint numbers, or are created as Unicode line-drawing characters by merging one or more effective line segments. .PP There are several advantages to using a \fBTickitRenderBuffer\fP over plain drawing requests directly to the terminal. Firstly, because the content is simply stored in memory until it is flushed to the terminal, it doesn't have to be rendered in screen order. It can be built up in any order that makes sense within the application, and when flushed to the terminal it will be performed in an efficient top-to-bottom, left-to-right order. .PP Secondly, the buffer understands horizontal and vertical line drawing using Unicode characters. While content is being built up, it will keep track of what kinds of lines meet in every cell in the buffer, so that when it is flushed to the terminal it can pick the appropriate Unicode line-drawing characters to render these with. .PP Thirdly, several features of the buffer are designed to easily support applications that divide the screen area into several possibly-overlapping regions that are managed by different parts of the application. Clipping, translation and masking support the concept of independent areas of the buffer, and stored pens and the state stack support the concept that these areas might be nested within each other, allowing rendering attributes to be inherited from outer regions into inner ones. .SS "VIRTUAL CURSOR" A \fBTickitRenderBuffer\fP instance maintains a virtual cursor position, that application code can use to render at. This is a virtual cursor, because it doesn't relate to the actual cursor in use on the terminal instance; it is simply a position stored by the buffer state. .PP Most of the content drawing functions come in pairs; one using and updating the cursor position, and a second that operates directly on the buffer contents, without regard to the virtual cursor. Functions of this latter form can be identified by the \fB_at\fP suffix on their name. .SS PEN A \fBTickitPen\fP instance can be set on a \fBTickitRenderBuffer\fP, acting as a default pen for subsequent drawing functions. This is optionally combined with a pen instance given to individual drawing functions; if both are present then the attributes are combined, with those of the given pen taking precedence over the ones in the stored pen. .SS TRANSLATION A translation offset can be applied to have the drawing functions store their output at some other location within the buffer. This translation only affects the drawing functions; the actual operation to flush the contents to the terminal is not affected. .SS CLIPPING AND MASKING All of the drawing functions are also subject to restriction of their output, to apply within a clipping region. Initially the entire buffer is available for drawing, but the area can be restricted to a smaller rectangular area at any time. Requests to draw content outside of this region will be ignored. .PP In addition to clipping, a buffer can also mask out arbitrary additional rectangular areas within the clipping region. These areas act with the clipping region by ignoring requests to draw inside them while preserving any existing content within them. .PP Masking and clipping are related but separate concepts. Both place restrictions on where output functions can alter the pending content. Whereas the clipping region is the rectangular area within which all drawing occurs, masking regions are areas in which drawing does \fInot\fP occur. .PP When combined with translation, these two features allow possibly-overlapping regions of content to be independently managed by separate pieces of code. To render each region of the screen, a render buffer can be set up with a translation offset and clipping rectangle to suit that region, thus avoiding the rendering code having to care about the exact on-screen geometry. By using masking regions, additionally these areas can be managed even when they overlap, by ensuring that areas already drawn by "higher" regions are masked off to ensure that "lower" regions do not overwrite them. .SS "SAVE STACK" As a further assistance to applications wishing to divide the screen area into nested regions, a set of functions exist to store the current auxilliary state of the buffer (that is, all of the mutable attributes listed above, but without the actual pending content) and later restore that state to its original values. .SH "FUNCTIONS" A new \fBTickitRenderBuffer\fP instance is created using \fBtickit_renderbuffer_new\fP(3). A render buffer instance stores a reference count to make it easier for applications to manage the lifetime of buffers. A new buffer starts with a count of one, and it can be adjusted using \fBtickit_renderbuffer_ref\fP(3) and \fBtickit_renderbuffer_unref\fP(3). When the count reaches zero the instance is destroyed. .PP Its size is fixed after creation and can be queried using \fBtickit_renderbuffer_get_size\fP(3). Its contents can be entirely reset back to its original state using \fBtickit_renderbuffer_reset\fP(3). .PP A translation offset can be set using \fBtickit_renderbuffer_translate\fP(3), and the clipping region restricted using \fBtickit_renderbuffer_clip\fP(3). Masks can be placed within the current clipping region using \fBtickit_renderbuffer_mask\fP(3). .PP The virtual cursor position can be set using \fBtickit_renderbuffer_goto\fP(3) and unset using \fBtickit_renderbuffer_ungoto\fP(3). It can be queried using \fBtickit_renderbuffer_has_cursorpos\fP(3) to determine if it is set, and \fBtickit_renderbuffer_get_cursorpos\fP(3) to return its position. A \fBTickitPen\fP instance can be set using \fBtickit_renderbuffer_setpen\fP(3). .PP The auxilliary state can be saved to the state stack using \fBtickit_renderbuffer_save\fP(3) and later restored using \fBtickit_renderbuffer_restore\fP(3). A stack state consisting of just the pen with no other state can be saved using \fBtickit_renderbuffer_savepen\fP(3). .PP The stored content of a buffer can be copied to another buffer using \fBtickit_renderbuffer_blit\fP(3). This is useful for allowing a window to maintain a backing buffer that can be drawn to at any time and then copied to a destination buffer for display. .PP The stored content can be flushed to a \fBTickitTerm\fP instance using \fBtickit_renderbuffer_flush_to_term\fP(3). .SH "DRAWING OPERATIONS" The following functions all affect the stored content within the buffer, taking into account the clipping, translation, masking, stored pen, and optionally the virtual cursor position. .PP \fBtickit_renderbuffer_skip_at\fP(3), \fBtickit_renderbuffer_skip\fP(3), \fBtickit_renderbuffer_skip_to\fP(3) and \fBtickit_renderbuffer_skiprect\fP(3) create a skipping region; a place where no output will be drawn. .PP \fBtickit_renderbuffer_text_at\fP(3) and \fBtickit_renderbuffer_text\fP(3) create a text region; a place where normal text is output. .PP \fBtickit_renderbuffer_erase_at\fP(3), \fBtickit_renderbuffer_erase\fP(3) and \fBtickit_renderbuffer_erase_to\fP(3) create an erase region; a place where existing terminal content will be erased. \fBtickit_renderbuffer_eraserect\fP(3) is a convenient shortcut that erases a rectangle, and \fBtickit_renderbuffer_clear\fP(3) erases the entire buffer area. .PP \fBtickit_renderbuffer_char_at\fP(3) and \fBtickit_renderbuffer_char\fP(3) place a single Unicode character directly. .PP \fBtickit_renderbuffer_hline_at\fP(3) and \fBtickit_renderbuffer_vline_at\fP(3) create horizontal and vertical line segments. .SH "SEE ALSO" .BR tickit (7), .BR tickit_pen (7), .BR tickit_rect (7) .BR tickit_term (7) libtickit-0.3.4/man/tickit_renderbuffer_blit.30000644000000000000000000000206713613430267017525 0ustar 00000000000000.TH TICKIT_RENDERBUFFER_BLIT 3 .SH NAME tickit_renderbuffer_blit \- copies buffer contents to another buffer .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_renderbuffer_blit(TickitRenderBuffer *" dst ", TickitRenderBuffer *" src ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_renderbuffer_blit\fP() copies the stored state in the \fIsrc\fP buffer to the \fIdst\fP buffer. The \fIsrc\fP buffer is not reset afterwards. Translation, clipping masks and current pen settings on the \fIdst\fP buffer are respected. .PP This function is intended for storing long-term screen state that rarely changes in an of-screen buffer stored by the application, allowing fast efficient rendering when required. Applications using this should be aware that memory allocated internally by the \fITickitRenderBuffer\fP instance is only release by \fBtickit_renderbuffer_reset\fP(3). .SH "RETURN VALUE" This function returns nothing. .SH "SEE ALSO" .BR tickit_renderbuffer_new (3), .BR tickit_renderbuffer_flush_to_term (3), .BR tickit_renderbuffer (7), .BR tickit (7) libtickit-0.3.4/man/tickit_renderbuffer_char.30000644000000000000000000000174713613430267017514 0ustar 00000000000000.TH TICKIT_RENDERBUFFER_CHAR 3 .SH NAME tickit_renderbuffer_char, tickit_renderbuffer_char_at \- create character regions .SH SYNOPSIS .EX .B #include .sp .BI "int tickit_renderbuffer_char(TickitRenderBuffer *" rb , .BI " long " codepoint ); .BI "int tickit_renderbuffer_char_at(TickitRenderBuffer *" rb , .BI " int " line ", int " col ", long " codepoint ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_renderbuffer_char\fP() creates a text region at the current virtual cursor position to contain the given Unicode character codepoint. .PP \fBtickit_renderbuffer_char_at\fP() creates a text region at the given position to contain the given Unicode character codepoint. This function does not use or update the virtual cursor position. .SH "RETURN VALUE" This function returns no value. .SH "SEE ALSO" .BR tickit_renderbuffer_new (3), .BR tickit_renderbuffer_blit (3), .BR tickit_renderbuffer_flush_to_term (3), .BR tickit_renderbuffer (7), .BR tickit (7) libtickit-0.3.4/man/tickit_renderbuffer_clip.30000644000000000000000000000273213613430267017521 0ustar 00000000000000.TH TICKIT_RENDERBUFFER_CLIP 3 .SH NAME tickit_renderbuffer_clip, tickit_renderbuffer_mask \- restrict the drawing area of output functions .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_renderbuffer_clip(TickitRenderBuffer *" rb ", TickitRect *" rect ); .BI "void tickit_renderbuffer_mask(TickitRenderBuffer *" rb ", TickitRect *" mask ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_renderbuffer_clip\fP() restricts the clipping rectangle to the limits given, within the existing limits already set. This function cannot make the clipping region larger than it already was. .PP \fBtickit_renderbuffer_mask\fP() applies a rectangular mask within the clipping region, masking off extra cells that can no longer be modified by the output functions. Unlike \fBtickit_renderbuffer_clip\fP() these regions can be arbitrarily positioned and discontinuous; each new call adds another masking region, rather than affecting the existing ones. .PP To undo the effects of any of these functions, they should be used within nested pairs of calls to \fBtickit_renderbuffer_save\fP(3) and \fBtickit_renderbuffer_restore\fP(3). Both functions only affect the subsequent drawing operations; they do not affect existing stored content, nor the behaviour of \fBtickit_renderbuffer_flush_to_term\fP(3). .SH "RETURN VALUE" These functions return no value. .SH "SEE ALSO" .BR tickit_renderbuffer_new (3), .BR tickit_renderbuffer_translate (3), .BR tickit_renderbuffer (7), .BR tickit (7) libtickit-0.3.4/man/tickit_renderbuffer_copyrect.30000644000000000000000000000235513613430267020423 0ustar 00000000000000.TH TICKIT_RENDERBUFFER_COPYRECT 3 .SH NAME tickit_renderbuffer_copyrect, tickit_renderbuffer_moverect \- copy or move a rectangular region of a buffer .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_renderbuffer_copyrect(TickitRenderBuffer *" rb ", .BI " const TickitRect *" dest ", const TickitRect *" src ); .BI "void tickit_renderbuffer_moverect(TickitRenderBuffer *" rb ", .BI " const TickitRect *" dest ", const TickitRect *" src ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_renderbuffer_copyrect\fP() copies the rectangular region in the buffer from the rectangle given by \fIsrc\fP into the location given by \fIdest\fP. .PP \fBtickit_renderbuffer_moverect\fP() moves content in the rectangular region in the buffer given by \fIsrc\fP into the location given by \fIdest\fP, resetting the region migrated out from back to the skip state. .PP In each case, the two regions may overlap. These functions will also copy cells in skip state; that is, cells set to skip in the source region are set to skip in the destination. .SH "RETURN VALUE" These functions return no value. .SH "SEE ALSO" .BR tickit_renderbuffer_new (3), .BR tickit_renderbuffer_flush_to_term (3), .BR tickit_renderbuffer (7), .BR tickit (7) libtickit-0.3.4/man/tickit_renderbuffer_erase.30000644000000000000000000000246213613430267017671 0ustar 00000000000000.TH TICKIT_RENDERBUFFER_ERASE 3 .SH NAME tickit_renderbuffer_erase, tickit_renderbuffer_erase_to, tickit_renderbuffer_erase_at \- create erase regions .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_renderbuffer_erase(TickitRenderBuffer *" rb , .BI " int " cols ); .BI "void tickit_renderbuffer_erase_to(TickitRenderBuffer *" rb , .BI " int " col ); .BI "void tickit_renderbuffer_erase_at(TickitRenderBuffer *" rb , .BI " int " line ", int " col ", int " cols ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_renderbuffer_erase\fP() creates a erase region that starts at the current virtual cursor position, continuing on for \fIcols\fP cells set to the current pen. \fBtickit_renderbuffer_erase_to\fP() creates a erase region that continues until the given column if the cursor was before that, or has no effect if it was after. Both of these functions will update the virtual cursor position. .PP \fBtickit_renderbuffer_erase_at\fP() creates a erase region at the given position and length. This function does not use or update the virtual cursor position. .SH "RETURN VALUE" These functions return nothing .SH "SEE ALSO" .BR tickit_renderbuffer_new (3), .BR tickit_renderbuffer_blit (3), .BR tickit_renderbuffer_flush_to_term (3), .BR tickit_renderbuffer (7), .BR tickit (7) libtickit-0.3.4/man/tickit_renderbuffer_eraserect.30000644000000000000000000000171313613430267020545 0ustar 00000000000000.TH TICKIT_RENDERBUFFER_ERASERECT 3 .SH NAME tickit_renderbuffer_eraserect, tickit_renderbuffer_clear \- erase large areas .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_renderbuffer_eraserect(TickitRenderBuffer *" rb , .BI " TickitRect *" rect ); .BI "void tickit_renderbuffer_clear(TickitRenderBuffer *" rb ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_renderbuffer_eraserect\fP() creates erase regions for every line in \fIrect\fP within the buffer, set to the current pen. .PP \fBtickit_renderbuffer_clear\fP() erases the entire buffer area within the current clipping region and masks, setting it all to be erase regions with the curent pen. .PP Neither of these functions use or update the virtual cursor. .SH "RETURN VALUE" These functions return nothing .SH "SEE ALSO" .BR tickit_renderbuffer_new (3), .BR tickit_renderbuffer_blit (3), .BR tickit_renderbuffer_flush_to_term (3), .BR tickit_renderbuffer (7), .BR tickit (7) libtickit-0.3.4/man/tickit_renderbuffer_flush_to_term.30000644000000000000000000000151213613430267021437 0ustar 00000000000000.TH TICKIT_RENDERBUFFER_FLUSH_TO_TERM 3 .SH NAME tickit_renderbuffer_flush_to_term \- output buffer contents to the terminal .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_renderbuffer_flush_to_term(TickitRenderBuffer *" rb ", TickitTerm *" tt ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_renderbuffer_flush_to_term\fP() outputs the entire stored state in the buffer to the terminal, then resets the buffer back to its initial state. Stored content is output in a strictly top-to-bottom, left-to-right order, ensuring a minimal amount of cursor movement for efficiency, and helping to reduce output flicker on the terminal display. .SH "RETURN VALUE" This function returns nothing. .SH "SEE ALSO" .BR tickit_renderbuffer_new (3), .BR tickit_renderbuffer_reset (3), .BR tickit_renderbuffer (7), .BR tickit (7) libtickit-0.3.4/man/tickit_renderbuffer_get_cursorpos.30000644000000000000000000000226113613430267021465 0ustar 00000000000000.TH TICKIT_RENDERBUFFER_GET_CURSORPOS 3 .SH NAME tickit_renderbuffer_has_cursorpos, tickit_renderbuffer_get_cursorpos \- query the virtual cursor position .SH SYNOPSIS .EX .B #include .sp .BI "bool tickit_renderbuffer_has_cursorpos(const TickitRenderBuffer *" rb ); .BI "void tickit_renderbuffer_get_cursorpos(const TickitRenderBuffer *" rb ", int " lines ", int " cols ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_renderbuffer_has_cursorpos\fP() returns a true value if the buffer's virtual cursor position has been set by \fBtickit_renderbuffer_goto\fP(3), or false if it has never been set, or was cleared by \fBtickit_renderbuffer_ungoto\fP(3) or \fBtickit_renderbuffer_reset\fP(3). .PP \fBtickit_renderbuffer_get_cursorpos\fP() retrieves the current position of the virtual cursor, by setting the integers pointed to by \fIline\fP and \fIcol\fP, if not NULL. If the virtual cursor position is not set then this function does nothing. .SH "RETURN VALUE" \fBtickit_renderbuffer_has_cursorpos\fP() returns a boolean. \fBtickit_renderbuffer_get_cursorpos\fP() returns no value. .SH "SEE ALSO" .BR tickit_renderbuffer_new (3), .BR tickit_renderbuffer (7), .BR tickit (7) libtickit-0.3.4/man/tickit_renderbuffer_get_size.30000644000000000000000000000120113613430267020371 0ustar 00000000000000.TH TICKIT_RENDERBUFFER_GET_SIZE 3 .SH NAME tickit_renderbuffer_get_size \- return the size of a render buffer .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_renderbuffer_get_size(const TickitRenderBuffer *" rb ", int *" lines ", int *" cols ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_renderbuffer_get_size\fP() sets the integers pointed to by \fIlines\fP and \fIcols\fP, if not \fBNULL\fP, to the size of the render buffer's content area. .SH "RETURN VALUE" \fBtickit_renderbuffer_get_size\fP() returns no value. .SH "SEE ALSO" .BR tickit_renderbuffer_new (3), .BR tickit_renderbuffer (7), .BR tickit (7) libtickit-0.3.4/man/tickit_renderbuffer_goto.30000644000000000000000000000252713613430267017544 0ustar 00000000000000.TH TICKIT_RENDERBUFFER_GOTO 3 .SH NAME tickit_renderbuffer_goto, tickit_renderbuffer_ungoto \- set or clear the virtual cursor position .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_renderbuffer_goto(TickitRenderBuffer *" rb ", int " line ", int " col ); .BI "void tickit_renderbuffer_ungoto(TickitRenderBuffer *" rb ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_renderbuffer_goto\fP() sets the virtual cursor position to the given line and column. This does not have to be within the bounds of either the buffer's extent, nor the clipping region. If it is moved outside of the clipping region, then no drawing operations will take effect, but the position will still be updated by these operations to check when it moves into the area of effect once more. This position is affected by the translation offset given by \fBtickit_renderbuffer_translate\fP(). .PP \fBtickit_renderbuffer_ungoto\fP() clears the virtual cursor position. After this call, the position is no longer defined, and the cursor-relative output functions will not operate. .SH "RETURN VALUE" These functions return no value. .SH "SEE ALSO" .BR tickit_renderbuffer_new (3), .BR tickit_renderbuffer_get_cursorpos (3), .BR tickit_renderbuffer_skip (3), .BR tickit_renderbuffer_text (3), .BR tickit_renderbuffer_erase (3), .BR tickit_renderbuffer (7), .BR tickit (7) libtickit-0.3.4/man/tickit_renderbuffer_hline_at.30000644000000000000000000000700613613430267020354 0ustar 00000000000000.TH TICKIT_RENDERBUFFER_HLINE_AT 3 .SH NAME tickit_renderbuffer_hline_at, tickit_renderbuffer_vline_at \- create line regions .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_renderbuffer_hline_at(TickitRenderBuffer *" rb , .BI " int " line ", int " startcol ", int " endcol , .BI " TickitLineStyle " style ", TickitLineCaps " caps ); .BI "void tickit_renderbuffer_vline_at(TickitRenderBuffer *" rb , .BI " int " startline ", int " endline ", int " col , .BI " TickitLineStyle " style ", TickitLineCaps " caps ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_renderbuffer_hline_at\fP() creates a horizontal line segment on the given line between the given columns; both inclusive. \fBtickit_renderbuffer_vline_at\fP() creates a vertical line segment on the given column between the given lines; both inclusive. .PP The \fIstyle\fP argument should be one of the following \fBTickitLineStyle\fP constants: .TP .B TICKIT_LINE_SINGLE A single thin line .TP .B TICKIT_LINE_DOUBLE A pair of double thin lines .TP .B TICKIT_LINE_THICK A single thick line .PP Note that as line drawing is performed using the Unicode line-drawing characters, not every possible combination of line segments of differing styles meeting in a cell is supported by Unicode. The following sets of styles may be relied upon: .IP * Any possible combination of only \fBSINGLE\fP segments, \fBTHICK\fP segments, or both. .IP * Any combination of only \fBDOUBLE\fP segments, except cells that only have one of the four borders occupied. .IP * Any combination of \fBSINGLE\fP and \fBDOUBLE\fP segments except where the style changes between \fBSINGLE\fP to \fBDOUBLE\fP on a vertical or horizontal run. .PP Other combinations are not directly supported (i.e. any combination of \fBDOUBLE\fP and \fBTHICK\fP in the same cell, or any attempt to change from \fBSINGLE\fP to \fBDOUBLE\fP in either the vertical or horizontal direction). To handle these cases, a cell may be rendered with a substitution character which replaces a \fBDOUBLE\fP or \fBTHICK\fP segment with a \fBSINGLE\fP one within that cell. The effect will be the overall shape of the line is retained, but close to the edge or corner it will have the wrong segment type. .PP Conceptually, every cell involved in line drawing has a potential line segment type at each of its four borders to its neighbours. Horizontal lines are drawn though the vertical centre of each cell, and vertical lines are drawn through the horizontal centre. .PP There is a choice of how to handle the ends of line segments, as to whether the segment should go to the centre of each cell, or should continue through the entire body of the cell and stop at the boundary. By default line segments will start and end at the centre of the cells, so that horizontal and vertical lines meeting in a cell will form a neat corner. When drawing isolated lines such as horizontal or vertical rules, it is preferrable that the line go right through the cells at the start and end. To control this behaviour, the \fIcaps\fP bitmask argument should be one of the following \fBTickitLineCaps\fP constants: .TP .B CAP_START indicates that the line should consume the entire start cell .TP .B CAP_END indicates that the line should consume the entire end cell .TP .B CAP_BOTH is a convenient shortcut specifying both behaviours .SH "RETURN VALUE" This function returns no value. .SH "SEE ALSO" .BR tickit_renderbuffer_new (3), .BR tickit_renderbuffer_blit (3), .BR tickit_renderbuffer_flush_to_term (3), .BR tickit_renderbuffer (7), .BR tickit (7) libtickit-0.3.4/man/tickit_renderbuffer_new.30000644000000000000000000000224413613430267017361 0ustar 00000000000000.TH TICKIT_RENDERBUFFER_NEW 3 .SH NAME tickit_renderbuffer_new \- create a new render buffer instance .SH SYNOPSIS .EX .B #include .sp .BI "TickitRenderBuffer *tickit_renderbuffer_new(int " lines ", int " cols ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_renderbuffer_new\fP() creates a new \fBTickitRenderBuffer\fP instance of the given size. Once constructed, the size is fixed. Initially it has a clipping region covering the entire area, a zero translation offset, no applied masks, no stored pen and no virtual cursor position. It can be reset back to this state at any time by calling \fBtickit_renderbuffer_reset\fP(3). .PP The reference count of a newly-constructed render buffer instance will be one. This can be incremented or decremented using \fBtickit_renderbuffer_ref\fP(3) and \fBtickit_renderbuffer_unref\fP(3). When its reference count reaches zero it is destroyed. .SH "RETURN VALUE" If successful, \fBtickit_renderbuffer_new\fP() returns a pointer to the new instance. .SH "SEE ALSO" .BR tickit_renderbuffer_text (3), .BR tickit_renderbuffer_blit (3), .BR tickit_renderbuffer_flush_to_term (3), .BR tickit_renderbuffer (7), .BR tickit (7) libtickit-0.3.4/man/tickit_renderbuffer_ref.30000644000000000000000000000175313613430267017350 0ustar 00000000000000.TH TICKIT_RENDERBUFFER_REF 3 .SH NAME tickit_renderbuffer_ref, tickit_renderbuffer_unref \- adjust the refcount of a render buffer .SH SYNOPSIS .EX .B #include .sp .BI "TickitRenderBuffer *tickit_renderbuffer_ref(TickitRenderBuffer *" rb ); .BI "void tickit_renderbuffer_unref(TickitRenderBuffer *" rb ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_renderbuffer_ref\fP() increments the stored refcount of the given render buffer instance by one. It returns the pointer argument itself, so it is useful for chaining. .PP \fBtickit_renderbuffer_unref\fP() decrements the stored refcount of the given render buffer instance by one. If the refcount drops to zero, the instance is destroyed. This will release any resources controlled by it. .SH "RETURN VALUE" \fBtickit_renderbuffer_ref\fP() returns a render buffer instance pointer. \fBtickit_renderbuffer_unref\fP() returns no value. .SH "SEE ALSO" .BR tickit_renderbuffer_new (3), .BR tickit_renderbuffer (7), .BR tickit (7) libtickit-0.3.4/man/tickit_renderbuffer_reset.30000644000000000000000000000174213613430267017714 0ustar 00000000000000.TH TICKIT_RENDERBUFFER_RESET 3 .SH NAME tickit_renderbuffer_reset \- revert a render buffer back to initial state .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_renderbuffer_reset(TickitRenderBuffer *" rb ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_renderbuffer_reset\fP() resets the render buffer back to its initial state. The pending content is erased, and all the cells are set back to the skipped state. The virtual cursor position is unset, the translation offset set back to zero, the clipping region set to the entire buffer and the pen removed if set. Also the saved state stack is cleared. .PP Any memory allocated for the storage of strings by the \fBtickit_renderbuffer_text\fP family of functions is also released by this function. .SH "RETURN VALUE" This function returns no value. .SH "SEE ALSO" .BR tickit_renderbuffer_new (3), .BR tickit_renderbuffer_save (3), .BR tickit_renderbuffer_restore (3), .BR tickit_renderbuffer (7), .BR tickit (7) libtickit-0.3.4/man/tickit_renderbuffer_save.30000644000000000000000000000331213613430267017523 0ustar 00000000000000.TH TICKIT_RENDERBUFFER_SAVE 3 .SH NAME tickit_renderbuffer_save, tickit_renderbuffer_restore, tickit_renderbuffer_savepen \- save and restore auxiliary state .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_renderbuffer_save(TickitRenderBuffer *" rb ); .BI "void tickit_renderbuffer_restore(TickitRenderBuffer *" rb ); .BI "void tickit_renderbuffer_savepen(TickitRenderBuffer *" rb ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_renderbuffer_save\fP() pushes a new entry to the buffer's saved state stack. This will capture the current values of the auxiliary state, which can be later restored using \fBtickit_renderbuffer_restore\fP(). .PP \fBtickit_renderbuffer_restore\fP() pops the most recent entry on the buffer's saved state stack and reverts the values of the auxiliary state back to the saved values. .PP \fBtickit_renderbuffer_savepen\fP() pushes a new entry to the stack containing just the current pen value. When it is restored, it will only restore the pen; the other attributes will be unaffected. .SH "AUXILIARY STATE" The state saved and restored by these functions is: .IP * the virtual cursor position .IP * the translation offset .IP * the clipping rectangle .IP * the current pen .IP * the set of masked regions .PP The pending content is not part of the state stack. It is intended that the state stack be used to help implement recursive drawing operations within the application, by dividing it into separate independent areas; allowing the state to be saved and restored between component parts. .SH "RETURN VALUE" None of these functions return a value. .SH "SEE ALSO" .BR tickit_renderbuffer_new (3), .BR tickit_renderbuffer_reset (3), .BR tickit_renderbuffer (7), .BR tickit (7) libtickit-0.3.4/man/tickit_renderbuffer_setpen.30000644000000000000000000000237213613430267020070 0ustar 00000000000000.TH TICKIT_RENDERBUFFER_SETPEN 3 .SH NAME tickit_renderbuffer_setpen \- change the stored rendering pen .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_renderbuffer_setpen(TickitRenderBuffer *" rb ", const TickitPen *" pen ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_renderbuffer_setpen\fP() changes the attributes of the stored pen in the buffer to those in the given \fBTickitPen\fP() instance, removing any that are not present in \fIpen\fP. The instance passed in the \fIpen\fP argument is not stored by the render buffer, nor is it modified by it. .PP If there is a stored pen in the previous stack level, then the attributes in that pen will be merged into the current one, before the passed one is applied. In effect, the actual pen used by drawing functions is merged from the pen argument passed to \fBtickit_renderbuffer_setpen\fP(), and the combined stored pen from the previous stack level (which was itself merged from the pen argument and the stored pen of the stack level below that, and so on). .SH "RETURN VALUE" This function returns nothing. .SH "SEE ALSO" .BR tickit_renderbuffer_new (3), .BR tickit_renderbuffer_text (3), .BR tickit_renderbuffer_save (3), .BR tickit_renderbuffer (7), .BR tickit_pen (7), .BR tickit (7) libtickit-0.3.4/man/tickit_renderbuffer_skip.30000644000000000000000000000274513613430267017544 0ustar 00000000000000.TH TICKIT_RENDERBUFFER_SKIP 3 .SH NAME tickit_renderbuffer_skip, tickit_renderbuffer_skip_at, tickit_renderbuffer_skip_to \- create skip regions .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_renderbuffer_skip(TickitRenderBuffer *" rb , .BI " int " cols ); .BI "void tickit_renderbuffer_skip_to(TickitRenderBuffer *" rb , .BI " int " col ); .BI "void tickit_renderbuffer_skip_at(TickitRenderBuffer *" rb , .BI " int " line ", int " col ", int " cols ); .sp .BI "void tickit_renderbuffer_skiprect(TickitRenderBuffer *" rb , .BI " TickitRect *" rect ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_renderbuffer_skip\fP() creates a skip region that starts at the current virtual cursor position, continuing on for \fIcols\fP cells. \fBtickit_renderbuffer_skip_to\fP() creates a skip region that continues until the given column if the cursor was before that, or has no effect if it was after. Both of these functions will update the virtual cursor position. .PP \fBtickit_renderbuffer_skip_at\fP() creates a skip region at the given position and length. This function does not use or update the virtual cursor position. .PP \fBtickit_renderbuffer_skiprect\fP() creates skip regions for every line in \fIrect\fP within the buffer. .SH "RETURN VALUE" These functions return nothing .SH "SEE ALSO" .BR tickit_renderbuffer_new (3), .BR tickit_renderbuffer_blit (3), .BR tickit_renderbuffer_flush_to_term (3), .BR tickit_renderbuffer (7), .BR tickit (7) libtickit-0.3.4/man/tickit_renderbuffer_text.30000644000000000000000000000532213613430267017554 0ustar 00000000000000.TH TICKIT_RENDERBUFFER_TEXT 3 .SH NAME tickit_renderbuffer_text, tickit_renderbuffer_text_at \- create text regions .SH SYNOPSIS .EX .B #include .sp .BI "int tickit_renderbuffer_text(TickitRenderBuffer *" rb , .BI " const char *" text ); .BI "int tickit_renderbuffer_textn(TickitRenderBuffer *" rb , .BI " const char *" text ", size_t " len ); .BI "int tickit_renderbuffer_textf(TickitRenderBuffer *" rb , .BI " const char *" fmt ", ...);" .BI "int tickit_renderbuffer_vtextf(TickitRenderBuffer *" rb , .BI " const char *" fmt ", va_list " args ); .sp .BI "int tickit_renderbuffer_text_at(TickitRenderBuffer *" rb , .BI " int " line ", int " col ", const char *" text ); .BI "int tickit_renderbuffer_textn_at(TickitRenderBuffer *" rb , .BI " int " line ", int " col ", const char *" text ", size_t " len ); .BI "int tickit_renderbuffer_textf_at(TickitRenderBuffer *" rb , .BI " int " line ", int " col ", const char *" fmt ", ...);" .BI "int tickit_renderbuffer_vtextf_at(TickitRenderBuffer *" rb , .BI " int " line ", int " col ", const char *" fmt ", va_list " args ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_renderbuffer_text\fP() creates a text region that starts at the current virtual cursor position, containing the given text string and set to the current pen. \fBtickit_renderbuffer_textn\fP() creates a text region of at most \fIlen\fP bytes. It returns the number of columns that the text string occupies. \fBtickit_renderbuffer_textf\fP() and \fBtickit_renderbuffer_vtextf\fP() take a format string in the style of \fBsprintf\fP(3) to create formatted text from either a list of arguments or a \fIva_list\fP. These functions will update the virtual cursor position. .PP \fBtickit_renderbuffer_text_at\fP(), \fBtickit_renderbuffer_textn_at\fP(), \fBtickit_renderbuffer_textf_at\fP() and \fBtickit_renderbuffer_vtextf_at\fP() create a text region at the given position, and otherwise operate analogously to their non-\fB_at\fP counterpart. These functions do not use or update the virtual cursor position. .PP Calls to any of these functions allocate storage owned by the \fITickitRenderBuffer\fP instance itself to store the strings. This storage is released again by \fBtickit_renderbuffer_reset\fP(3), the implicit reset that happens at the end of \fBtickit_renderbuffer_flush_to_term\fP(3), or when every cell that was originally part of the region has been overwritten with other content. .SH "RETURN VALUE" These functions return an integer giving the number of columns the new region occupies. .SH "SEE ALSO" .BR tickit_renderbuffer_new (3), .BR tickit_renderbuffer_blit (3), .BR tickit_renderbuffer_flush_to_term (3), .BR tickit_renderbuffer (7), .BR tickit (7) libtickit-0.3.4/man/tickit_renderbuffer_translate.30000644000000000000000000000214113613430267020561 0ustar 00000000000000.TH TICKIT_RENDERBUFFER_TRANSLATE 3 .SH NAME tickit_renderbuffer_translate \- move the base of output functions .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_renderbuffer_translate(TickitRenderBuffer *" rb ", int " downward ", int " rightward ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_renderbuffer_translate\fP() applies a translation to the co-ordinate system used by \fBtickit_renderbuffer_goto\fP(3) and the \fB_at\fP-suffixed absolute-position drawing functions, and returned by \fBtickit_renderbuffer_get_cursorpos\fP(3). .PP To undo the effects of any of this functions, it should be used within nested pairs of calls to \fBtickit_renderbuffer_save\fP(3) and \fBtickit_renderbuffer_restore\fP(3). This function only affects the subsequent drawing operations; it do not affect existing stored content, nor the behaviour of \fBtickit_renderbuffer_flush_to_term\fP(3). .SH "RETURN VALUE" These functions return no value. .SH "SEE ALSO" .BR tickit_renderbuffer_new (3), .BR tickit_renderbuffer_clip (3), .BR tickit_renderbuffer_mask (3), .BR tickit_renderbuffer (7), .BR tickit (7) libtickit-0.3.4/man/tickit_run.30000644000000000000000000000337513613430267014651 0ustar 00000000000000.TH TICKIT_RUN 3 .SH NAME tickit_run, tickit_stop \- control the event loop of the toplevel instance .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_run(Tickit *" t ); .BI "void tickit_stop(Tickit *" t ); .sp .BI "void tickit_tick(Tickit *" t ", TickitRunFlags " flags ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_run\fP() starts the IO event loop of the main toplevel instance, allowing it to receive and react to terminal input, and to send output content back to it. Once invoked, this function will block processing IO and not return until the \fBtickit_stop\fP() function is called, or until a \fBSIGINT\fP is received (usually by the user pressing \fICtrl-C\fP). .PP \fBtickit_stop\fP() causes a currently-running call to \fBtickit_run\fP() to return. .PP \fBtickit_tick\fP() runs one iteration of the IO loop and returns. Its behaviour is controlled by the \fIflags\fP argument, which should be a bitmask of the following flags: .in .TP .B TICKIT_RUN_NOHANG Perform only a single round of IO without blocking. \fBtickit_tick\fP() will check for IO, or timers that are ready, handle those, and return. but will not block further waiting if nothing is yet ready. .TP .B TICKIT_RUN_NOSETUP Do not perform any terminal setup before waiting on IO events. Usually this is only necessary early in startup, if the program is using the Tickit event loop for its own purposes before starting a terminal interface. .IP Without this flag, terminal setup will be run once the first time \fBtickit_run\fP or \fBtickit_tick\fP is called, and will remain valid until the toplevel instance is destroyed. .SH "RETURN VALUE" Neither \fBtickit_run\fP(), \fBtickit_stop\fP() nor \fBtickit_tick\fP() return a value. .SH "SEE ALSO" .BR tickit_new_stdio (3), .BR tickit (7) libtickit-0.3.4/man/tickit_setctl_int.30000644000000000000000000000173713613430267016215 0ustar 00000000000000.TH TICKIT_SETCTL_INT 3 .SH NAME tickit_setctl_int\- set an integer toplevel instance control .SH SYNOPSIS .EX .B #include .sp .BI "bool tickit_getctl_int(Tickit *" tt ", TickitCtl " ctl ", int *" value ); .BI "bool tickit_setctl_int(Tickit *" tt ", TickitCtl " ctl ", int " value ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_setctl_int\fP() performs a toplevel instance control operation, setting the value of a numeric instance control option. \fBtickit_getctl_int\fP() obtains the value of an instance control setting. .PP For the list of possible \fBTickitCtl\fP values and their meanings, see \fBtickit\fP(7). .SH "RETURN VALUE" \fBtickit_getctl_int\fP() returns a true value if it recognised the requested control and managed to return the current value of it; false if not. \fBtickit_setctl_int\fP() returns a true value if it recognised the requested control and managed to request the instance to change it; false if not. .SH "SEE ALSO" .BR tickit (7) libtickit-0.3.4/man/tickit_string.70000644000000000000000000000205613613430267015352 0ustar 00000000000000.TH TICKIT_STRING 7 .SH NAME TickitString \- a reference-counted string buffer .SH SYNOPSIS .EX .B #include .sp .BI "typedef struct " TickitString ; .EE .sp .SH DESCRIPTION A \fBTickitString\fP instance stores a NUL-terminated character buffer (i.e. a plain C string) and a reference count. It allows string buffers to be efficiently shared while their usage is tracked, and reclaimed once no longer required. .SH FUNCTIONS A new \fBTickitString\fP instance is created by calling \fBtickit_string_new\fP(3). Once constructed, its buffer can be read by calling \fBtickit_string_get\fP(3) and its length queried by \fBtickit_string_len\fP(3). The buffer should be considered immutable; it cannot be modified. .PP A string instance maintains a reference count to make it easier for applications to share and manage the lifetime of these buffers. A new string starts with a count of one, and it can be adjusted using \fBtickit_string_ref\fP(3) and \fBtickit_string_unref\fP(3). When the count reaches zero the instance is destroyed. .SH "SEE ALSO" .BR tickit (7) libtickit-0.3.4/man/tickit_string_get.30000644000000000000000000000120313613430267016176 0ustar 00000000000000.TH TICKIT_STRING_GET 3 .SH NAME tickit_string_get \- obtain the characters from a counted string .SH SYNOPSIS .EX .B #include .sp .BI "const char *tickit_string_get(const TickitString *" s ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_string_get\fP() returns a pointer to the actual stored character buffer within the counted string instance. This should be considered immutable; the caller must not modify this buffer. .SH "RETURN VALUE" \fBtickit_string_get\fP() returns a NUL-terminated string pointer. .SH "SEE ALSO" .BR tickit_string_new (3), .BR tickit_string_len (3), .BR tickit_string (7), .BR tickit (7) libtickit-0.3.4/man/tickit_string_len.30000644000000000000000000000103313613430267016176 0ustar 00000000000000.TH TICKIT_STRING_LEN 3 .SH NAME tickit_string_get \- query the length of a counted string .SH SYNOPSIS .EX .B #include .sp .BI "size_t tickit_string_len(const TickitString *" s ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_string_len\fP() returns the size in bytes of the stored character buffer within the counted string instance. .SH "RETURN VALUE" \fBtickit_string_len\fP() returns a size in bytes. .SH "SEE ALSO" .BR tickit_string_new (3), .BR tickit_string_get (3), .BR tickit_string (7), .BR tickit (7) libtickit-0.3.4/man/tickit_string_new.30000644000000000000000000000144113613430267016214 0ustar 00000000000000.TH TICKIT_STRING_NEW 3 .SH NAME tickit_string_new \- create a counted string .SH SYNOPSIS .EX .B #include .sp .BI "TickitString *tickit_string_new(const char *" str ", size_t " len ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_string_new\fP() creates a new \fBTickitString\fP instance. It will be initialised with the contents of the given string buffer. .PP The reference count of a newly-constructed string instance will be one. This can be incremented or decremented using \fBtickit_string_ref\fP(3) and \fBtickit_string_unref\fP(3). When its reference count reaches zero it is destroyed. .SH "RETURN VALUE" If successful, \fBtickit_string_new\fP() returns a pointer to the new instance. .SH "SEE ALSO" .BR tickit_string_get (3), .BR tickit_string (7), .BR tickit (7) libtickit-0.3.4/man/tickit_string_ref.30000644000000000000000000000154713613430267016206 0ustar 00000000000000.TH TICKIT_STRING_REF 3 .SH NAME tickit_string_ref, tickit_string_unref \- adjust the refcount of a counted string .SH SYNOPSIS .EX .B #include .sp .BI "TickitString *tickit_string_ref(TickitString *" s ); .BI "void tickit_string_unref(TickitString *" s ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_string_ref\fP() increments the stored refcount of the given counted string instance by one. It returns the pointer argument itself, so it is useful for chaining. .PP \fBtickit_string_unref\fP() decrements the stored refcount of the given counted string instance by one. If the refcount drops to zero, the instance is destroyed. .SH "RETURN VALUE" \fBtickit_string_ref\fP() returns a counted string instance pointer. \fBtickit_string_unref\fP() returns no value. .SH "SEE ALSO" .BR tickit_string_new (3), .BR tickit_string (7), .BR tickit (7) libtickit-0.3.4/man/tickit_stringpos_limit_bytes.30000644000000000000000000000312413613430267020471 0ustar 00000000000000.TH TICKIT_STRINGPOS_LIMIT_BYTES 3 .SH NAME tickit_stringpos_limit_... \- set limit fields in string position counters .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_stringpos_limit_bytes(TickitStringPos *" pos ", size_t " bytes ); .BI "void tickit_stringpos_limit_codepoints(TickitStringPos *" pos ", int " codepoints ); .BI "void tickit_stringpos_limit_graphemes(TickitStringPos *" pos ", int " graphemes ); .BI "void tickit_stringpos_limit_columns(TickitStringPos *" pos ", int " columns ); .BI "void tickit_stringpos_limit_none(TickitStringPos *" pos ); .sp .BI "#define INIT_TICKIT_STRINGPOS_LIMIT_BYTES(" bytes ) .BI "#define INIT_TICKIT_STRINGPOS_LIMIT_CODEPOINTS(" codepoints ) .BI "#define INIT_TICKIT_STRINGPOS_LIMIT_GRAPHEMES(" grahpemes ) .BI "#define INIT_TICKIT_STRINGPOS_LIMIT_COLUMNS(" columns ) .BI "#define INIT_TICKIT_STRINGPOS_LIMIT_NONE" .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION The first four of these functions each set one of the counter fields in \fIpos\fP to the given value, and the other three to -1. This is useful to create a limit counter to stop \fBtickit_utf8_count\fP(3) or \fBtickit_utf8_countmore\fP(3) at the given position. The final function initialises all four fields to -1. .PP Each is also available as a macro which can be used to initialise a new \fBTickitStringPos\fP variable. .PP .EX TickitStringPos limit = INIT_TICKIT_STRINGPOS_LIMIT_BYTES(b); .EE .SH "RETURN VALUE" The functions all return no value. The macros expand to a struct initialiser expression. .SH "SEE ALSO" .BR tickit_utf8_count (3), .BR tickit_stringpos_zero (3), .BR tickit (7) libtickit-0.3.4/man/tickit_stringpos_zero.30000644000000000000000000000131713613430267017126 0ustar 00000000000000.TH TICKIT_STRINGPOS_ZERO 3 .SH NAME tickit_stringpos_zero \- clear the fields of a string position counter .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_stringpos_zero(TickitStringPos *" pos ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_stringpos_zero\fP() sets the four counter fields of \fIpos\fP to zero. This operation is done implictly by \fBtickit_utf8_count\fP(3), but this function may be useful to initialise a counter to zeros before entering a loop, and using \fBtickit_utf8_countmore\fP(3) for every iteration. .SH "RETURN VALUE" \fBtickit_stringpos_zero\fP() returns no value. .SH "SEE ALSO" .BR tickit_utf8_count (3), .BR tickit_stringpos_limit_bytes (3), .BR tickit (7) libtickit-0.3.4/man/tickit_term.70000644000000000000000000002441113613430267015012 0ustar 00000000000000.TH TICKIT_TERM 7 .SH NAME TickitTerm \- abstraction of an interactive terminal .SH SYNOPSIS .EX .B #include .sp .BI "typedef struct " TickitTerm ; .EE .sp .SH DESCRIPTION A \fBTickitTerm\fP instance represents an interactive user terminal. It provides functions to draw content to the terminal, and to accept input and other events from it. It supports a variety of modes of operation; allowing both synchronous and asynchronous filehandle IO, and working abstractly via byte buffers. .SH FUNCTIONS A new \fBTickitTerm\fP instance is created using the \fBtickit_term_new\fP(3) or \fBtickit_term_new_for_termtype\fP(3) functions. A terminal instance stores a reference count to make it easier for applications to manage the lifetime of terminals. A new terminal starts with reference count of one, and it can be adjusted using \fBtickit_term_ref\fP(3) and \fBtickit_term_unref\fP(3). When the count reaches zero the instance is destroyed. .PP The \fBtickit_term_open_stdio\fP(3) function offers a convenient shortcut to creating a new instance set up to represent the standard input and output streams of the process. .PP A terminal instance will need either an output function or an output filehandle set before it can send output. This can be performed by either \fBtickit_term_set_output_func\fP(3) or \fBtickit_term_set_output_fd\fP(3). An output buffer can be defined by \fBtickit_term_set_output_buffer\fP(3). If output is via a filehandle, then the size of that will be queried if it is a .SM TTY. If output is via an output function only then the size must be set using \fBtickit_term_set_size\fP(3). An input filehandle can be set using \fBtickit_term_set_input_fd\fP(3), or input can be sent from a byte buffer using \fBtickit_term_input_push_bytes\fP(3). Once input and output methods are set the terminal startup actions are performed, and the \fBtickit_term_await_started_msec\fP(3) function can be used to wait until this is complete. A running instance can be paused using \fBtickit_term_pause\fP(3) and resumed using \fBtickit_term_resume\fP(3). .PP It supports .SM UTF-8 if enabled; either by detection of a .SM UTF-8 locale, explicitly by calling \fBtickit_term_set_utf8\fP(3). .PP The size of the terminal can be queried using \fBtickit_term_get_size\fP(3), or forced to a given size by \fBtickit_term_set_size\fP(3). If the application is aware that the size of a terminal represented by a \fBtty\fP(7) filehandle has changed (for example due to receipt of a \fBSIGWINCH\fP signal), it can call \fBtickit_term_refresh_size\fP(3) to update it. The type of the terminal is set at construction time but can be queried later using \fBtickit_term_get_termtype\fP(3). .SH OUTPUT Once an output method is defined, a terminal instance can be used for outputting drawing and other commands. For drawing, the functions \fBtickit_term_print\fP(3), \fBtickit_term_goto\fP(3), \fBtickit_term_move\fP(3), \fBtickit_term_scrollrect\fP(3), \fBtickit_term_chpen\fP(3), \fBtickit_term_setpen\fP(3), \fBtickit_term_clear\fP(3) and \fBtickit_term_erasech\fP(3) can be used. Additionally for setting modes, the function \fBtickit_term_setctl_int\fP(3) can be used. If an output buffer is defined it will need to be flushed when drawing is complete by calling \fBtickit_term_flush\fP(3). .SH INPUT Input via a filehandle can be received either synchronously by calling \fBtickit_term_input_wait_msec\fP(3), or asynchronously by calling \fBtickit_term_input_readable\fP(3) and \fBtickit_term_input_check_timeout_msec\fP(3). Any of these functions may cause one or more events to be raised by invoking event handler functions. .SH EVENTS A terminal instance stores a list of event handlers. Each event handler is associated with one event type and stores a function pointer, and an arbitrary pointer containing user data. Event handlers may be installed using \fBtickit_term_bind_event\fP(3) and removed using \fBtickit_term_unbind_event_id\fP(3). .PP Fake events can be artificially injected into the event handler chain, as if they had been received from the controlling terminal, by \fBtickit_term_emit_key\fP(3) and \fBtickit_term_emit_mouse\fP(3). These may be useful for testing, event capture-and-replay, or other specialised cases. .PP The event types recognised are: .TP .B TICKIT_TERM_ON_DESTROY The terminal instance is being destroyed. .TP .B TICKIT_TERM_ON_RESIZE The terminal has been resized. \fIinfo\fP will point to a structure defined as: .sp .EX .B typedef struct { .BI " int " lines ; .BI " int " cols ; .BI "} " TickitResizeEventInfo ; .EE .TP .B TICKIT_TERM_ON_KEY A key has been pressed on the keyboard. \fIinfo\fP will point to a structure defined as: .sp .EX .B typedef struct { .BI " TickitKeyEventType " type ; .BI " int " mod ; .BI " const char *" str ; .BI "} " TickitKeyEventInfo ; .EE .IP \fItype\fP is an enumeration that gives the specific type of key event. .RS .TP .B TICKIT_KEYEV_KEY a cursor control, arrow key, or function key. i.e. any of the keys that don't directly produce text. .TP .B TICKIT_KEYEV_TEXT regular Unicode characters. .RE .sp \fIstr\fP will contain the name of the special key, including any applied modifiers, or a .SM UTF-8 string of the Unicode character. .sp \fImod\fP will contain a bitmask of \fBTICKIT_MOD_SHIFT\fP, \fBTICKIT_MOD_ALT\fP and \fBTICKIT_MOD_CTRL\fP. .sp This event only runs until a bound function returns a true value; this prevents later handler functions from observing it. .TP .B TICKIT_TERM_ON_MOUSE A mouse button has been pressed or released, the mouse cursor moved while dragging a button, or the wheel has been scrolled. \fIinfo\fP will point to a structure defined as: .sp .EX .B typedef struct { .BI " TickitMouseEventType " type ; .BI " int " button ; .BI " int " mod ; .BI " int " line ; .BI " int " col ; .BI "} " TickitMouseEventInfo ; .EE .IP \fItype\fP is an enumeration that gives the specific type of mouse event. .RS .TP .B TICKIT_MOUSEEV_PRESS A mouse button has been pressed. .TP .B TICKIT_MOUSEEV_DRAG The mouse has been moved while a button is being held down. .TP .B TICKIT_MOUSEEV_RELEASE A mouse button has been released. .TP .B TICKIT_MOUSEEV_WHEEL The wheel has been rolled. .RE .sp \fIbutton\fP gives the button index for button events, or one of \fBTICKIT_MOUSEWHEEL_UP\fP or \fBTICKIT_MOUSEWHEEL_DOWN\fP for wheel events. .sp \fIline\fP and \fIcol\fP give the position of the mouse cursor for this event. .sp \fImod\fP will contain a bitmask of \fBTICKIT_MOD_SHIFT\fP, \fBTICKIT_MOD_ALT\fP and \fBTICKIT_MOD_CTRL\fP. .sp This event only runs until a bound function returns a true value; this prevents later handler functions from observing it. .SH CONTROLS A terminal instance has a number of runtime-configuration control options that affect its behaviour. These can be set using \fBtickit_term_setctl_int\fP(3) and \fBtickit_term_setctl_str\fP(3), and queried using \fBtickit_term_getctl_int\fP(3). The individual controls have human-readable string names that can be obtained by \fBtickit_term_ctlname\fP(3) and searched by name using \fBtickit_term_lookup_ctl\fP(3). The type of a control option can be queried using \fBtickit_term_ctltype\fP(3). .PP The options are given in an enumeration called \fBTickitTermCtl\fP. The following control values are recognised: .in .TP .B TICKIT_TERMCTL_ALTSCREEN (bool) The value is a boolean indicating whether the terminal alternate buffer mode should be enabled. When enabled, a temporary buffer is used for drawing, preserving the original contents of the screen. This mode is usually used by full-screen applications to preserve the shell's scrollback state. .TP .B TICKIT_TERMCTL_COLORS (int, read-only) The value indicates how many colors are available. This value is read-only; it can be requested but not set. .IP On terminfo-driven terminals this will likely be 8, 16, or 256. On \fIxterm\fP-like terminals this will be 16,777,216 (i.e. \f(Cw1 << 24\fP) if the driver detects that the terminal supports 24-bit RGB8 ("true\-color") palettes, or 256 if not. .TP .B TICKIT_TERMCTL_CURSORBLINK (bool) The value is a boolean indicating whether the terminal text cursor should blink. When disabled, the cursor will appear in a steady state, if visible. When enabled, the cursor will appear blinking, if visible. If the cursor is invisible, this should not have any effect. .TP .B TICKIT_TERMCTL_CURSORSHAPE (int) The value is an integer from the \fBTickitCursorShape\fP enumeration indicating what shape the terminal's text cursor should be. Values are: .RS .TP .B TICKIT_CURSORSHAPE_BLOCK A solid block filling the entire cell. .TP .B TICKIT_CURSORSHAPE_UNDER An underline below the character. .TP .B TICKIT_CURSORSHAPE_LEFT_BAR A vertical bar to the left of the character. .RE .IP Note that not all terminals support setting this option, nor to all of the possible values. .TP .B TICKIT_TERMCTL_CURSORVIS (bool) The value is a boolean indicating whether the terminal text cursor should be visible. When disabled the cursor position is not visible. Typically applications will hide the cursor while performing redrawing operations so as not to show a flickering effect as the cursor moves, and show it again when drawing is complete. .TP .B TICKIT_TERMCTL_ICON_TEXT (str) The value is a string for the terminal to use as its minimised icon text. .TP .B TICKIT_TERMCTL_ICONTITLE_TEXT (str) The value is a string for the terminal to use as its minimised icon text and main window title. .TP .B TICKIT_TERMCTL_KEYPAD_APP (bool) The value is a boolean controlling the terminal's keypad mode. When enabled, the terminal is in keypad application mode; in this mode the numerical keypad will send different sequences that can be detected as distinct from regular .SM ASCII text. When disabled, the keypad will send normal text. .TP .B TICKIT_TERMCTL_MOUSE (int) The value is an integer from the \fBTickitTermMouseMode\fP enumeration indicating what mouse events should be sent. Values are: .RS .TP .B BTICKIT_TERM_MOUSEMODE_CLICK Report button press and release events. .TP .B TICKIT_TERM_MOUSEMODE_DRAG Report button press and release events, and movement while a button is held. .TP .B TICKIT_TERM_MOUSEMODE_MOVE Report all button press, release and motion events even with no buttons held. .TP .B TICKIT_TERM_MOUSEMODE_OFF Report nothing. .RE .TP .B TICKIT_TERMCTL_TITLE_TEXT (str) The value is a string for the terminal to use as its main window title. .SH "SEE ALSO" .BR tickit (7), .BR tickit_renderbuffer (7) libtickit-0.3.4/man/tickit_term_await_started_msec.30000644000000000000000000000326613613430267020735 0ustar 00000000000000.TH TICKIT_TERM_AWAIT_STARTED_MSEC 3 .SH NAME tickit_term_await_started_* \- wait until the terminal is initialised .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_term_await_started_msec(TickitTerm *" tt ", long" msec ); .BI "void tickit_term_await_started_tv(TickitTerm *" tt ", const struct timeval *" timeout ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION The \fBtickit_term_await_started_*\fP() family of functions wait until the terminal driver announces it has completed setting up the terminal, and it is ready to be used. The timeout specifies the maximum time to wait; the function will return after this time even if the terminal is not ready yet. One of these functions should be called after the relevant input and output methods have been set on the terminal instance. .PP The functions differ in how the timeout is specified. \fBtickit_term_await_started_msec\fP() takes a time as an integer in miliseconds, or -1 to wait indefinitely. \fBtickit_term_await_started_tv\fP() takes a time as a \fIstruct timeval\fP, or \fBNULL\fP to wait indefinitely. .PP Under most terminal drivers it is not strictly required that it be completely prepared before it is used, as preparation consists mainly of detecting optionally-supported features the terminal may have. If the application starts outputting before this is finished, it simply may not make use of some features, or not detect or report that some features are present. .SH "RETURN VALUE" \fBtickit_term_await_started_msec\fP() and \fBtickit_term_await_started_tv\fP() return no value. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_set_output_func (3), .BR tickit_term_set_output_fd (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_bind_event.30000644000000000000000000000267513613430267017213 0ustar 00000000000000.TH TICKIT_TERM_BIND_EVENT 3 .SH NAME tickit_term_bind_event, tickit_term_unbind_event_id \- add or remove event handlers .SH SYNOPSIS .EX .B #include .sp .BI "typedef int " TickitTermEventFn "(TickitTerm *" tt ", TickitEventFlags " flags , .BI " void *" info ", void *" user ); .sp .BI "int tickit_term_bind_event(TickitTerm *" tt ", TickitTermEvent " ev , .BI " TickitBindFlags " flags , .BI " TickitTermEventFn *" fn ", void *" user ); .BI "void tickit_term_unbind_event_id(TickitTerm *" tt ", int " id ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_bind_event\fP() adds a new event handler to the list of handlers stored by the terminal, and returns an integer to identify this handler. This handler will be invoked for occurrances of the event given by the \fIev\fP argument. When invoked, \fIfunc\fP will be passed the terminal instance, a flags bitmask, a pointer to an event information structure whose type depends on the event, and the user data pointer it was installed with. .PP \fBtickit_term_unbind_event_id\fP() removes an event handler previously added, by the identifier returned when it was added, invoking it with the \fBTICKIT_EV_UNBIND\fP flag if it was installed with \fBTICKIT_BIND_UNBIND\fP. .SH "RETURN VALUE" \fBtickit_term_bind_event\fP() returns an identifier integer. \fBtickit_term_unbind_event_id\fP() returns no value. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_chpen.30000644000000000000000000000251413613430267016163 0ustar 00000000000000.TH TICKIT_TERM_CHPEN 3 .SH NAME tickit_term_chpen, tickit_term_setpen \- set rendering attributes of the terminal .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_term_chpen(TickitTerm *" tt ", TickitPen *" pen ); .BI "void tickit_term_setpen(TickitTerm *" tt ", TickitPen *" pen ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_chpen\fP() changes the rendering attributes currently in effect in the terminal by setting those attributes specified in the given \fBTickitPen\fP instance. Attributes not present in \fIpen\fP remain unmodified by this operation. .PP \fBtickit_term_setpen\fP() changes the rendering attributes currently in effect in the terminal by setting all of the attributes to those given in the \fBTickitPen\fP instance. Any attribute not present in \fIpen\fP is reset back to its default value. .PP If the foreground or background colour is set to an index higher than the terminal can support, it will be converted to the best choice that is available from the palette of 8 or 16 colors. .SH "RETURN VALUE" \fBtickit_term_chpen\fP() and \fBtickit_term_setpen\fP() return no value. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_set_output_fd (3), .BR tickit_term_set_output_func (3), .BR tickit_term_goto (3), .BR tickit_term_print (3), .BR tickit_term (7), .BR tickit_pen (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_clear.30000644000000000000000000000110113613430267016143 0ustar 00000000000000.TH TICKIT_TERM_CLEAR 3 .SH NAME tickit_term_clear \- clear the terminal .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_term_clear(TickitTerm *" tt ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_clear\fP() clear the entire content of the terminal. .SH "RETURN VALUE" \fBtickit_term_clear\fP() returns no value. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_set_output_fd (3), .BR tickit_term_set_output_func (3), .BR tickit_term_goto (3), .BR tickit_term_print (3), .BR tickit_term_erasech (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_ctlname.30000644000000000000000000000303513613430267016510 0ustar 00000000000000.TH TICKIT_TERM_CTLNAME 3 .SH NAME tickit_term_ctlname \- return the name of a terminal control .SH SYNOPSIS .EX .B #include .sp .BI "const char * tickit_term_ctlname(TickitTermCtl " ctl ); .BI "TickitTermCtl tickit_term_lookup_ctl(const char * " name ); .sp .BI "TickitType tickit_term_ctltype(TickitTermCtl " ctl ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_ctlname\fP() returns the name of the given terminal control. These names are the lowercase strings given in \fBtickit_term\fP(7). .PP \fBtickit_term_lookup_ctl\fP() returns the control constant for a given name. If the name is not recognised then -1 is returned. .PP \fBtickit_term_ctltype\fP() returns the type of the given terminal control, as one of the following constants: .in .TP .B TICKIT_TYPE_BOOL The value is an integer representing a simple boolean choice, where any nonzero value is true. Use \fBtickit_term_setctl_int\fP(3) and \fBtickit_term_getctl_int\fP(3). .TP .B TICKIT_TYPE_INT The value is an integer representing a quantity, or a value in some particular enumeration. Use \fBtickit_term_setctl_int\fP(3) and \fBtickit_term_getctl_int\fP(3). .TP .B TICKIT_TYPE_STR The value is a string. Use \fBtickit_term_setctl_str\fP(3). .PP For unrecognised control types, the function returns 0. .SH "RETURN VALUE" \fBtickit_term_ctlname\fP() returns a constant string pointer. \fBtickit_term_lookup_ctl\fP() returns an control constant or -1. \fBtickit_term_ctltype\fP() returns a control type constant or 0. .SH "SEE ALSO" .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_emit_key.30000644000000000000000000000204013613430267016666 0ustar 00000000000000.TH TICKIT_TERM_EMIT_KEY 3 .SH NAME tickit_term_emit_key, tickit_term_emit_mouse \- emit a fake key or mouse event .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_term_emit_key(TickitTerm *" tt ", TickitKeyEventInfo *" info ); .BI "void tickit_term_emit_mouse(TickitTerm *" tt ", TickitMouseEventInfo *" info ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_emit_key\fP() invokes the event handler chain on the terminal instance, as if a key event had been received from the controlling terminal.\fBtickit_term_emit_mouse\fP() invokes the event handler chain on the terminal instance, as if a mouse event had been received from the controlling terminal. .PP In each case, the event handler chain runs with the usual semantics; each installed handler with the appropriate bit set in its event type mask will be invoked until one returns a true value. Any remaining handlers after this will not get to see the event. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_bind_event (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_erasech.30000644000000000000000000000264613613430267016506 0ustar 00000000000000.TH TICKIT_TERM_ERASECH 3 .SH NAME tickit_term_erasech \- erase characters from the terminal .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_term_erasech(TickitTerm *" tt ", int " count ", TickitMaybeBool " moveend ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_erasech\fP() erases \fIcount\fP character cells forward from the current cursor location, using the current pen background colour. .PP Some terminals cannot erase using the background colour, so this operation may be implemented by printing spaces on such terminals. This will move the cursor to the end of the erased region. Other terminals that do erase with background colour can be erased without moving the cursor. The \fImoveend\fP parameter controls the behaviour of the cursor location when this function returns. If set to \fBTICKIT_YES\fP, the cursor will be moved to the end of the erased region if required. If set to \fBTICKIT_NO\fP, the cursor will be moved back to its original location if required. If set to \fBTICKIT_MAYBE\fP, this function will take whichever behaviour is more optimal on the given terminal. .SH "RETURN VALUE" \fBtickit_term_erasech\fP() returns no value. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_set_output_fd (3), .BR tickit_term_set_output_func (3), .BR tickit_term_goto (3), .BR tickit_term_print (3), .BR tickit_term_setpen (3), .BR tickit_term_chpen (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_flush.30000644000000000000000000000151513613430267016207 0ustar 00000000000000.TH TICKIT_TERM_FLUSH 3 .SH NAME tickit_term_flush \- flush the terminal output buffer .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_term_flush(TickitTerm *" tt ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_flush\fP() flushes any data pending in the output buffer to the terminal, either by calling the output function if defined by \fBtickit_term_set_output_func\fP(3), or by \fBwrite\fP(3) on the output file descriptor defined by \fBtickit_term_set_output_fd\fP(3). If there is no pending data then this function does nothing. .SH "RETURN VALUE" \fBtickit_term_flush\fP() returns no value. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_set_output_fd (3), .BR tickit_term_set_output_func (3), .BR tickit_term_set_output_buffer (3), .BR tickit_term_print (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_get_size.30000644000000000000000000000233513613430267016700 0ustar 00000000000000.TH TICKIT_TERM_GET_SIZE 3 .SH NAME tickit_term_get_size, tickit_term_set_size, tickit_term_refresh_size \- manage terminal size .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_term_get_size(TickitTerm *" tt ", int *" lines ", int *" cols ); .BI "void tickit_term_set_size(TickitTerm *" tt ", int " lines ", int " cols ); .BI "void tickit_term_refresh_size(TickitTerm *" tt ); .EE .sp Link with \fI-ltickit\fP. .SH DESCRIPTION \fBtickit_term_get_size\fP() fetches the currently-known size of the terminal into the two integers supplied by pointers. .PP \fBtickit_term_set_size\fP() stores a new size for the terminal into the instance, and invokes any \fBTICKIT_EV_RESIZE\fP event handlers. .PP \fBtickit_term_refresh_size\fP() queries the size of the terminal by using the \fBTIOCGWINSZ\fP \fBioctl\fP(2) call on the currently-associated output filehandle, and invokes any \fBTICKIT_EV_RESIZE\fP event handlers if the size has changed. .SH "RETURN VALUE" \fBtickit_term_get_size\fP, \fBtickit_term_set_size\fP and \fBtickit_term_refresh_size\fP return no value. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_set_output_fd (3), .BR tickit_term_goto (3), .BR tickit_term_bind_event (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_get_termtype.30000644000000000000000000000140713613430267017576 0ustar 00000000000000.TH TICKIT_TERM_GET_TERMTYPE 3 .SH NAME tickit_term_get_termtype \- returns the terminal type .SH SYNOPSIS .EX .B #include .sp .BI "const char *tickit_term_get_termtype(TickitTerm *" tt ); .EE .sp Link with \fI-ltickit\fP. .SH DESCRIPTION \fBtickit_term_get_termtype\fP() returns the string name of the type of the terminal. This is the string that was given to \fBtickit_term_new_for_termtype\fP(3), or was fetched from the \fBTERM\fP environment variable. The returned value points directly into the buffer allocated by the \fITickitTerm\fP instance itself; this buffer must not be modified or freed. .SH "RETURN VALUE" \fBtickit_term_get_termtype\fP() returns a string buffer pointer. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_goto.30000644000000000000000000000246613613430267016044 0ustar 00000000000000.TH TICKIT_TERM_GOTO 3 .SH NAME tickit_term_goto, tickit_term_move \- move the terminal output cursor .SH SYNOPSIS .EX .B #include .sp .BI "bool tickit_term_goto(TickitTerm *" tt ", int " line ", int " col ); .BI "void tickit_term_move(TickitTerm *" tt ", int " downward ", int " rightward ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_goto\fP() moves the terminal output cursor to the absolute position specified. On some terminals, either \fIline\fP or \fIcol\fP may be specified as -1 to move within the line or column it is currently in. Not all terminals may support the partial move ability; so the return value of \fBtickit_term_goto\fP() should be checked after attempting a goto within the line or column to see if it actually worked. If not, the application will have to reset the position using a fully-specified goto. .PP \fBtickit_term_move\fP() moves the terminal output cursor relative to its current position. Either \fIdownward\fP or \fIrightward\fP may be specified as 0 to not move in that direction. .SH "RETURN VALUE" \fBtickit_term_goto\fP() returns a boolean value indicating whether it was able to support the requested movement. \fBtickit_term_move\fP() returns no value. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_print (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_input_check_timeout_msec.30000644000000000000000000000225513613430267022141 0ustar 00000000000000.TH TICKIT_TERM_INPUT_CHECK_TIMEOUT_MSEC 3 .SH NAME tickit_term_input_check_timeout_msec \- terminal timeout behaviour .SH SYNOPSIS .EX .B #include .sp .BI "int tickit_term_input_check_timeout_msec(TickitTerm *" tt ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_input_check_timeout_msec\fP() performs input-related timeout behaviour, used to handle multi-byte input events, the Escape key, and other things. It returns a value indicating the number of miliseconds of delay before it wishes to be called again. When called again, it may perform any timeout behaviours that are required, and either return another timeout delay, or -1 to indicate there is no longer a need for timeout. Calling it may result in \fBTICKIT_EV_KEY\fP events. .PP This function also invokes deferred \fBTICKIT_EV_RESIZE\fP events if enabled by \fBtickit_term_observe_sigwinch\fP(3). .SH "RETURN VALUE" \fBtickit_term_input_check_timeout_msec\fP() returns a number indicating a timeout delay in miliseconds. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_set_input_fd (3), .BR tickit_term_input_push_bytes (3), .BR tickit_term_bind_event (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_input_push_bytes.30000644000000000000000000000161413613430267020472 0ustar 00000000000000.TH TICKIT_TERM_INPUT_PUSH_BYTES 3 .SH NAME tickit_term_input_push_bytes \- supply more input data .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_term_input_push_bytes(TickitTerm *" tt ", const char *" bytes ", size_t " len ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_push_input_bytes\fP() supplies more data to the terminal instance, as if it had been read from a terminal file descriptor. Calling this function may result in \fBTICKIT_EV_KEY\fP or \fBTICKIT_EV_MOUSE\fP events being invoked. .PP This function also invokes deferred \fBTICKIT_EV_RESIZE\fP events if enabled by \fBtickit_term_observe_sigwinch\fP(3). .SH "RETURN VALUE" \fBtickit_term_input_push_bytes\fP() returns no value. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_set_input_fd (3), .BR tickit_term_input_readable (3), .BR tickit_term_bind_event (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_input_readable.30000644000000000000000000000175513613430267020052 0ustar 00000000000000.TH TICKIT_TERM_INPUT_READABLE 3 .SH NAME tickit_term_input_readable \- read more data from the terminal .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_term_input_readable(TickitTerm *" tt ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_input_readable\fP() informs the terminal instance that more data is, or may be, available on the input file descriptor previously set with \fBtickit_term_set_input_fd\fP(), and reads it. Calling this function may block if the underlying file descriptor is in blocking mode. It may result in \fBTICKIT_EV_KEY\fP or \fBTICKIT_EV_MOUSE\fP events being invoked. .PP This function also invokes deferred \fBTICKIT_EV_RESIZE\fP events if enabled by \fBtickit_term_observe_sigwinch\fP(3). .SH "RETURN VALUE" \fBtickit_term_input_readable\fP() returns no value. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_set_input_fd (3), .BR tickit_term_input_push_bytes (3), .BR tickit_term_bind_event (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_input_wait_msec.30000644000000000000000000000252513613430267020262 0ustar 00000000000000.TH TICKIT_TERM_INPUT_WAIT_MSEC 3 .SH NAME tickit_term_input_wait_* \- read more data from the terminal .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_term_input_wait_msec(TickitTerm *" tt ", long " msec ); .BI "void tickit_term_input_wait_tv(TickitTerm *" tt ", const struct timeval *" timeout ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION The \fBtickit_term_input_wait_*\fP() family of functions wait for at least one input event to be received, up until the maximum time given. Calling these functions may block if the underlying file descriptor is in blocking mode. They may result in \fBTICKIT_EV_KEY\fP or \fBTICKIT_EV_MOUSE\fP events being invoked. .PP These functions also invoke deferred \fBTICKIT_EV_RESIZE\fP events if enabled by \fBtickit_term_observe_sigwinch\fP(3). .PP The functions differ in how the timeout is specified. \fBtickit_term_input_wait_msec\fP() takes a time as an integer in miliseconds, or -1 to wait indefinitely. \fBtickit_term_input_wait_tv\fP() takes a time as a \fIstruct timeval\fP, or \fBNULL\fP to wait indefinitely. .SH "RETURN VALUE" \fBtickit_term_input_wait_msec\fP() and \fBtickit_term_input_wait_tv\fP() return no value. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_set_input_fd (3), .BR tickit_term_input_readable (3), .BR tickit_term_bind_event (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_new.30000644000000000000000000000232013613430267015652 0ustar 00000000000000.TH TICKIT_TERM_NEW 3 .SH NAME tickit_term_new \- create a new terminal instance .SH SYNOPSIS .EX .B #include .sp .BI "TickitTerm *tickit_term_new(void);" .BI "TickitTerm *tickit_term_new_for_termtype(const char *" termtype ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_new\fP() creates a new \fBTickitTerm\fP instance for the terminal type given by the \fBTERM\fP environment variable. .PP \fBtickit_term_new_for_termtype\fP() creates a new \fBTickitTerm\fP instance for the given terminal type. .PP The reference count of a newly-constructed terminal instance will be one. This can be incremented or decremented using \fBtickit_term_ref\fP(3) and \fBtickit_term_unref\fP(3). When its reference count reaches zero it is destroyed. .SH "RETURN VALUE" If successful, \fBtickit_term_new\fP() and \fBtickit_term_new_for_termtype\fP() return a pointer to the new instance. On failure, \fBNULL\fP is returned with \fIerrno\fP set to indicate the failure. .SH "SEE ALSO" .BR tickit_term_open_stdio (3), .BR tickit_term_set_output_fd (3), .BR tickit_term_set_output_func (3), .BR tickit_term_set_input_fd (3), .BR tickit_term_print (3), .BR tickit_term_bind_event (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_observe_sigwinch.30000644000000000000000000000356013613430267020430 0ustar 00000000000000.TH TICKIT_TERM_OBSERVE_SIGWINCH 3 .SH NAME tickit_term_observe_sigwinch \- react to the SIGWINCH signal .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_term_observe_sigwinch(TickitTerm *" tt ", bool " observe ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_observe_sigwinch\fP() controls a setting on the \fITickitTerm\fP instance that allows it to be informed when the process receives a \fBSIGWINCH\fP signal, meaning that the controlling terminal may have changed size. .PP When this flag is enabled on at least one terminal instance, a handler for the \fBSIGWINCH\fP signal is automatically installed by \fIlibtickit\fP itself. On receipt of the signal the handler will mark a flag on any \fITickitTerm\fP instance that is observing it to indicate that the window may have changed size. The next time these instances invoke any of several input-related functions (listed below), this flag will cause the function to first perform the actions of \fBtickit_term_refresh_size\fP(3). This deferred handling ensures that the \fBTICKIT_EV_RESIZE\fP event is only invoked at a time when the application is expecting input events, and not asynchronously during the signal handler itself. .PP Note that use of this ability requires giving control of the process-wide \fBSIGWINCH\fP signal to the \fIlibtickit\fP library. If this is not suitable, the process should arrange to invoke \fBtickit_term_refresh_size\fP() at an appropriate time by some other mechanism. .SH "EVENT-DEFERRAL FUNCTIONS" .IP * \fBtickit_term_input_push_bytes\fP(3) .IP * \fBtickit_term_input_readable\fP(3) .IP * The \fBtickit_term_input_check_timeout_msec\fP(3) family .IP * The \fBtickit_term_input_wait_msec\fP(3) family .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_set_input_fd (3), .BR tickit_term_refresh_size (3), .BR tickit_term_bind_event (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_open_stdio.30000644000000000000000000000201513613430267017225 0ustar 00000000000000.TH TICKIT_TERM_OPEN_STDIO 3 .SH NAME tickit_term_open_stdio \- create a terminal instance on standard input/output .SH SYNOPSIS .EX .B #include .sp .BI "TickitTerm *tickit_term_open_stdio(void);" .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_open_stdio\fP() creates a new \fBTickitTerm\fP instance to represent the standard input and output streams of the process. This function is a convenient shortcut around the common use-case of creating an instance using \fBtickit_term_new\fP(3), setting the input and output handles using \fBtickit_term_set_input_fd\fP(3) and \fBtickit_term_set_output_fd\fP(3), and enabling \fBSIGWINCH\fP support using \fBtickit_term_observe_sigwinch\fP(3). .SH "RETURN VALUE" If successful, \fBtickit_term_open_stdio\fP() returns a pointer to the new instance. On failure, \fBNULL\fP is returned with \fIerrno\fP set to indicate the failure. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_print (3), .BR tickit_term_bind_event (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_pause.30000644000000000000000000000236013613430267016202 0ustar 00000000000000.TH TICKIT_TERM_PAUSE 3 .SH NAME tickit_term_pause, tickit_term_resume \- pause and resume a terminal instance .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_term_pause(TickitTerm *" tt ); .BI "void tickit_term_resume(TickitTerm *" tt ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_pause\fP() suspends the operation of the terminal by resetting any modes or other persistent state on it as if shutting down. Any modes set within the object instance are remembered however. .PP \fBtickit_term_resume\fP() returns the terminal back to the state it was operating in before \fBtickit_term_pause\fP() was called, allowing the program to continue as normal. .PP It is intended these functions be used to create a program-wide suspend feature, where the terminal can be handed back over to the invoking shell while the process backgrounds itself. Typically this is done by the process sending itself a \fBSIGSTOP\fP signal via \fBraise\fP(3). After calling \fBtickit_term_pause\fP(), no other terminal-related functions should be called, nor other IO operations attempted, until after a subsequent \fBtickit_term_resume\fP(3). .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_setctl_int (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_print.30000644000000000000000000000254513613430267016226 0ustar 00000000000000.TH TICKIT_TERM_PRINT 3 .SH NAME tickit_term_print \- send text to the terminal .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_term_print(TickitTerm *" tt ", const char *" str ); .BI "void tickit_term_printn(TickitTerm *" tt ", const char *" str ", size_t " len ); .sp .BI "void tickit_term_printf(TickitTerm *" tt ", const char *" fmt ", ...);" .BI "void tickit_term_vprintf(TickitTerm *" tt ", const char *" fmt ", va_list " args ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_print\fP() sends a string of text to the terminal to be printed at the current cursor location. The string must be free from any control characters. \fBtickit_term_printn\fP() sends a string at most \fIlen\fP characters to be printed. .PP \fBtickit_term_printf\fP() sends a string of text built by formatting the given arguments in the same way that \fBprintf\fP(3) does. \fBtickit_term_vprintf\fP() is similar, taking its arguments instead in a \fBva_list\fP as \fBvprintf\fP(3) does. .SH "RETURN VALUE" \fBtickit_term_print\fP(), \fBtickit_term_printf\fP() and \fBtickit_term_vprintf\fP() return no value. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_set_output_fd (3), .BR tickit_term_set_output_func (3), .BR tickit_term_goto (3), .BR tickit_term_setpen (3), .BR tickit_term_chpen (3), .BR tickit_term_erasech (3) .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_ref.30000644000000000000000000000204413613430267015640 0ustar 00000000000000.TH TICKIT_TERM_REF 3 .SH NAME tickit_term_ref, tickit_term_unref \- adjust the refcount of a terminal .SH SYNOPSIS .EX .B #include .sp .BI "TickitTerm *tickit_term_ref(TickitTerm *" tt ); .BI "void tickit_term_unref(TickitTerm *" tt ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_ref\fP() increments the stored refcount of the given terminal instance by one. It returns the pointer argument itself, so it is useful for chaining. .PP \fBtickit_term_unref\fP() decrements the stored refcount of the given terminal instance by one. If the refcount drops to zero, the instance is destroyed. This will release any resources controlled by it and unbind all the bound event handlers, causing handlers to be invoked with the \fBTICKIT_EV_DESTROY\fP flag if appropriate. It will not close any filehandles associated with the instance. .SH "RETURN VALUE" \fBtickit_term_ref\fP() returns a terminal instance pointer. \fBtickit_term_unref\fP() returns no value. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_scrollrect.30000644000000000000000000000217313613430267017243 0ustar 00000000000000.TH TICKIT_TERM_SCROLLRECT 3 .SH NAME tickit_term_scrollrect \- scroll a region of the terminal .SH SYNOPSIS .EX .B #include .sp .BI "bool tickit_term_scrollrect(TickitTerm *" tt ", TickitRect " rect ", .BI " int " downward ", int " rightward ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_scrollrect\fP() attempts to scroll a rectangular region of the terminal by a given offset, efficiently moving content within the display. Whether or not this is possible depends on the type of terminal and what region and offset were specified. This function returns a true value if this operation was successful. If it was not possible to perform the scroll, it will return false without modifying the terminal. In this situation, the application must fall back to re-drawing the affected region with new content. .SH "RETURN VALUE" \fBtickit_term_scrollrect\fP() returns a boolean indicating if the scrolling operation was successful. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_goto (3), .BR tickit_term_print (3), .BR tickit_term_setpen (3), .BR tickit_term_chpen (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_set_input_fd.30000644000000000000000000000240413613430267017547 0ustar 00000000000000.TH TICKIT_TERM_SET_INPUT_FD 3 .SH NAME tickit_term_set_input_fd, tickit_term_get_input_fd \- manage terminal input .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_term_set_input_fd(TickitTerm *" tt ", int " fd ); .BI "int tickit_term_get_input_fd(TickitTerm *" tt ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_set_input_fd\fP() associates an input file descriptor with the terminal instance. This file descriptor will be used by \fBtickit_term_input_readable\fP(3) to read more data from the terminal. The value -1 may be set to indicate an absence of a file descriptor. If no file descriptor is provided, input data may still be given by calling \fBtickit_term_input_push_bytes\fP(3). .PP \fBtickit_term_get_input_fd\fP() returns the currently associated input file descriptor, or -1 if none has been set. .PP After both an input and output method have been defined, it is recommended to call \fBtickit_term_await_started\fP(3) to wait for the terminal to be set up. .SH "RETURN VALUE" \fBtickit_term_set_input_fd\fP() returns no value. \fBtickit_term_get_input_fd\fP() returns a file descriptor or -1. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_await_started (3), .BR tickit_term_bind_event (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_set_output_buffer.30000644000000000000000000000232413613430267020631 0ustar 00000000000000.TH TICKIT_TERM_SET_OUTPUT_BUFFER 3 .SH NAME tickit_term_set_output_buffer \- define a terminal output buffer .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_term_set_output_buffer(TickitTerm *" tt ", size_t " len ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_set_output_buffer\fP() sets up an output buffer of the given size, to store output bytes pending being written to the output file descriptor, or using the output function. The value 0 may be given to indicate that no buffer should be used. This buffer is not directly accessible. .PP If a buffer is defined, then none of the drawing functions will immediately create output either to the file descriptor or the output function. Instead, they will append into the buffer, to be flushed by calling \fBtickit_term_flush\fP(3). If the buffer fills completely as a result of being written to by a drawing function, it will be flushed to the appropriate output method. .SH "RETURN VALUE" \fBtickit_term_set_output_buffer\fP() returns no value. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_set_output_fd (3), .BR tickit_term_set_output_func (3), .BR tickit_term_print (3), .BR tickit_term_flush (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_set_output_fd.30000644000000000000000000000260513613430267017753 0ustar 00000000000000.TH TICKIT_TERM_SET_OUTPUT_FD 3 .SH NAME tickit_term_set_output_fd, tickit_term_get_output_fd \- manage terminal output .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_term_set_output_fd(TickitTerm *" tt ", int " fd ); .BI "int tickit_term_get_output_fd(TickitTerm *" tt ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_set_output_fd\fP() associates an output file descriptor with the terminal instance. If no output function has been set by \fBtickit_term_set_output_func\fP(3) then this file descriptor will be used to send bytes to the user's terminal. This file descriptor will also be used by \fBtickit_term_refresh_size\fP(3) to query the size of the terminal by issuing the \fBTIOCGWINSZ\fP \fBioctl\fP(2). The value -1 may be set to indicate an absence of a file descriptor. .PP \fBtickit_term_get_output_fd\fP() returns the currently associated output file descriptor, or -1 if none has been set. .PP After both an input and output method have been defined, it is recommended to call \fBtickit_term_await_started\fP(3) to wait for the terminal to be set up. .SH "RETURN VALUE" \fBtickit_term_set_output_fd\fP() returns no value. \fBtickit_term_get_output_fd\fP() returns a file descriptor or -1. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_set_output_func (3), .BR tickit_term_await_started (3), .BR tickit_term_print (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_set_output_func.30000644000000000000000000000264713613430267020323 0ustar 00000000000000.TH TICKIT_TERM_SET_OUTPUT_FUNC 3 .SH NAME tickit_term_set_output_func \- manage terminal output via a callback function .SH SYNOPSIS .EX .B #include .sp .BI "typedef void " TickitTermOutputFunc "(TickitTerm *" tt ", const char *" bytes , .BI " size_t " len ", void *" user ); .sp .BI "void tickit_term_set_output_func(TickitTerm *" tt , .BI " TickitTermOutputFunc *" fn ", void *" user ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_set_output_func\fP() associates an output function with the terminal instance. If set, this function will be used to send bytes to the user's terminal even if an output file descriptor is also set. When the function is invoked, it will be passed the terminal instance, a byte buffer and size, and the user data pointer it was installed with. .PP When an output function is replaced by another or the terminal is destroyed, an existing function is invoked one final time to inform it. In this case, the \fIlen\fP argument will be 0 and \fIbytes\fP will be \fBNULL\fP. .PP After both an input and output method have been defined, it is recommended to call \fBtickit_term_await_started\fP(3) to wait for the terminal to be set up. .SH "RETURN VALUE" \fBtickit_term_set_output_func\fP() returns no value. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term_set_output_fd (3), .BR tickit_term_await_started (3), .BR tickit_term_print (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_set_utf8.30000644000000000000000000000176213613430267016633 0ustar 00000000000000.TH TICKIT_TERM_SET_UTF8 3 .SH NAME tickit_term_set_utf8, tickit_term_get_utf8 \- control .SM UTF-8 mode .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_term_set_utf8(TickitTerm *" tt ", bool " utf8 ); .BI "TickitMaybeBool tickit_term_get_utf8(TickitTerm *" tt ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_set_utf8\fP() controls whether terminal input is processed according to .SM UTF-8 rules, or legacy 8-bit single byte rules. If this function is not called explicitly, it will be detected automatically by inspecting locale variables. .PP \fBtickit_term_get_utf8\fP() returns the current value for .SM UTF-8 support as \fBTICKIT_YES\fP or \fBTICKIT_NO\fP, or \fBTICKIT_MAYBE\fP if the value is not yet known and has not been explicitly set. .SH "RETURN VALUE" \fBtickit_term_set_utf8\fP() returns no value. \fBtickit_term_get_utf8\fP() returns a \fITickitMaybeBool\fP tri-state boolean value. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_term_setctl_int.30000644000000000000000000000246613613430267017244 0ustar 00000000000000.TH TICKIT_TERM_SETCTL_INT 3 .SH NAME tickit_term_setctl_int, tickit_term_setctl_str \- set an integer or string terminal control .SH SYNOPSIS .EX .B #include .sp .BI "bool tickit_term_getctl_int(TickitTerm *" tt ", TickitTermCtl " ctl ", int *" value ); .BI "bool tickit_term_setctl_int(TickitTerm *" tt ", TickitTermCtl " ctl ", int " value ); .BI "bool tickit_term_setctl_str(TickitTerm *" tt ", TickitTermCtl " ctl ", const char *" value ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_term_setctl_int\fP() performs a terminal control operation, setting the value of a numeric terminal control option. \fBtickit_term_setctl_str\fP() sets the value of a string terminal control option. \fBtickit_term_getctl_int\fP() obtains the value of a terminal control setting. .PP For the list of possible \fBTickitTermCtl\fP values and their meanings, see \fBtickit_term\fP(7). .SH "RETURN VALUE" \fBtickit_term_getctl_int\fP() returns a true value if it recognised the requested control and managed to return the current value of it; false if not. \fBtickit_term_setctl_int\fP() and \fBtickit_term_setctl_str\fP() return a true value if it recognised the requested control and managed to request the terminal to change it; false if not. .SH "SEE ALSO" .BR tickit_term_new (3), .BR tickit_term (7), .BR tickit (7) libtickit-0.3.4/man/tickit_utf8_count.30000644000000000000000000001036613613430267016141 0ustar 00000000000000.TH TICKIT_UTF8_COUNT 3 .SH NAME tickit_utf8_count, tickit_utf8_countmore \- count characters in Unicode strings .SH SYNOPSIS .EX .B #include .sp .B "typedef struct {" .BI " size_t " bytes ; .BI " int " codepoints ; .BI " int " graphemes ; .BI " int " columns ; .BI "} " TickitStringPos ; .sp .BI "size_t tickit_utf8_count(const char *" str ", TickitStringPos *" pos , .BI " const TickitStringPos *" limit ); .BI "size_t tickit_utf8_countmore(const char *" str ", TickitStringPos *" pos , .BI " const TickitStringPos *" limit ); .sp .BI "size_t tickit_utf8_ncount(const char *" str ", size_t " len , .BI " TickitStringPos *" pos ", const TickitStringPos *" limit ); .BI "size_t tickit_utf8_ncountmore(const char *" str ", size_t " len , .BI " TickitStringPos *" pos ", const TickitStringPos *" limit ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_utf8_count\fP() counts characters in the given Unicode string, which must be in .SM UTF-8 encoding. It starts at the beginning of the string and counts forward over codepoints and graphemes, incrementing the counters in \fIpos\fP until it reaches a limit. It will not go further than any of the limits given by the \fIlimits\fP structure (where the value -1 indicates no limit of that type). It will never split a codepoint in the middle of a .SM UTF-8 sequence, nor will it split a grapheme between its codepoints; it is therefore possible that the function returns before any of the limits have been reached, if the next whole grapheme would involve going past at least one of the specified limits. The function will also stop when it reaches the end of \fIstr\fP. It returns the total number of bytes it has counted over. .PP The \fIbytes\fP member counts .SM UTF-8 bytes which encode individual codepoints. For example the Unicode character .SM U+00E9 is encoded by two bytes 0xc3, 0xa9; it would increment the \fIbytes\fP counter by 2 and the \fIcodepoints\fP counter by 1. .PP .PP The \fIcodepoints\fP member counts individual Unicode codepoints. .PP The \fIgraphemes\fP member counts whole composed graphical clusters of codepoints, where combining accents which count as individual codepoints do not count as separate graphemes. For example, the codepoint sequence .SM "U+0065 U+0301" would increment the \fIcodepoint\fP counter by 2 and the \fIgraphemes\fP counter by 1. .PP The \fIcolumns\fP member counts the number of screen columns consumed by the graphemes. Most graphemes consume only 1 column, but some are defined in Unicode to consume 2. .PP \fBtickit_utf8_countmore\fP() is similar to \fBtickit_utf8_count\fP() except it will not zero any of the counters before it starts. It can continue counting where a previous call finished. In particular, it will assume that it is starting at the beginning of a .SM UTF-8 sequence that begins a new grapheme; it will not check these facts and the behavior is undefined if these assumptions do not hold. It will begin at the offset given by \fIpos.bytes\fP. .PP The \fBtickit_utf8_ncount\fP() and \fBtickit_utf8_ncountmore\fP() variants are similar except that they read no more than \fIlen\fP bytes from the string and do not require it to be .SM NUL terminated. They will still stop at a .SM NUL byte if one is found before \fIlen\fP bytes have been read. .PP These functions will all immediately abort if any C0 or C1 control byte other than .SM NUL is encountered, returning the value -1. In this circumstance, the \fIpos\fP structure will still be updated with the progress so far. .SH USAGE Typically, these functions would be used either of two ways. .PP When given a value in \fIlimit.bytes\fP (or no limit and simply using string termination), \fBtickit_utf8_count\fP() will yield the width of the given string in terminal columns, in the \fIlimit.columns\fP field. .PP When given a value in \fIlimit.columns\fP, \fBtickit_utf8_count\fP() will yield the number of bytes of that string that will consume the given space on the terminal. .SH "RETURN VALUE" \fBtickit_utf8_count\fP() and \fBtickit_utf8_countmore\fP() return the number of bytes they have skipped over this call, or -1 if they encounter a C0 or C1 byte other than .SM NUL . .SH "SEE ALSO" .BR tickit_stringpos_zero (3), .BR tickit_stringpos_limit_bytes (3), .BR tickit_utf8_mbswidth (3), .BR tickit (7) libtickit-0.3.4/man/tickit_utf8_mbswidth.30000644000000000000000000000226013613430267016624 0ustar 00000000000000.TH TICKIT_UTF8_MBSWIDTH 3 .SH NAME tickit_utf8_mbswidth, tickit_utf8_byte2col, tickit_utf8_col2byte \- count the column width of a Unicode string .SH SYNOPSIS .EX .B #include .sp .BI "int tickit_utf8_mbswidth(const char *" str ); .sp .BI "int tickit_utf8_byte2col(const char *" str ", size_t " byte ); .BI "size_t tickit_utf8_col2byte(const char *" str ", "int " col ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION These functions are a set of shortcut wrappers around \fBtickit_utf8_count\fP(3) for performing a single simple counting operation on a given string. When performing multiple operations on a given string, it is more efficient to use \fBtickit_utf8_count\fP() directly. .PP \fBtickit_utf8_mbswidth\fP() returns the number of columns wide the given Unicode string consumes. .PP \fBtickit_utf8_byte2col\fP() and \fBtickit_utf8_col2byte\fP() convert between byte and column indexes within a given string; returning one when given the other. .SH "RETURN VALUE" \fBtickit_utf8_mbswidth\fP() and \fBtickit_utf8_byte2col\fP() return an integer column count. \fBtickit_utf8_col2byte\fP() returns an integer byte count. .SH "SEE ALSO" .BR tickit_utf8_count (3), .BR tickit (7) libtickit-0.3.4/man/tickit_utf8_put.30000644000000000000000000000211113613430267015606 0ustar 00000000000000.TH TICKIT_UTF8_PUT 3 .SH NAME tickit_utf8_put \- append a UTF-8 encoded codepoint to a buffer .SH SYNOPSIS .EX .B #include .sp .BI "size_t tickit_utf8_put(char *" str ", size_t " len ", long " codepoint ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_utf8_put\fP() appends bytes into a buffer to represent the given codepoint. The .SM UTF-8 bytes will be appended directly starting from \fIstr\fP, and the function then returns the total number of bytes appended. The length of the buffer should be given to as \fIlen\fP; if the buffer is not long enough then it is left unmodified and -1 is returned instead. If \fIstr\fP is NULL then the function will simply return the number of bytes it would need to append to represent the codepoint. .PP This function does not expect the buffer to be NUL-terminated, and it will not terminate the buffer with a NUL byte on completion. .SH "RETURN VALUE" \fBtickit_utf8_put\fP() returns the number of bytes appended to the buffer, or -1 if the buffer was not long enough. .SH "SEE ALSO" .BR tickit_utf8_seqlen (3), .BR tickit (7) libtickit-0.3.4/man/tickit_utf8_seqlen.30000644000000000000000000000077113613430267016277 0ustar 00000000000000.TH TICKIT_UTF8_SEQLEN 3 .SH NAME tickit_utf8_seqlen \- determine the length of a UTF-8 codepoint encoding .SH SYNOPSIS .EX .B #include .sp .BI "int tickit_utf8_seqlen(long " codepoint ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_utf8_seqlen\fP() determines the number of bytes required in the .SM UTF-8 encoding of the given codepoint and returns it. .SH "RETURN VALUE" \fBtickit_utf8_seqlen\fP() returns a byte count. .SH "SEE ALSO" .BR tickit_utf8_put (3), .BR tickit (7) libtickit-0.3.4/man/tickit_version.70000644000000000000000000000134713613430267015533 0ustar 00000000000000.TH TICKIT_VERSION 7 .SH NAME tickit_version_* \- \fIlibtickit\fP version queries .SH SYNOPSIS .EX .B #define TICKIT_VERSION_MAJOR .B #define TICKIT_VERSION_MINOR .B #define TICKIT_VERSION_PATCH .sp .BI "int tickit_version_major(void);" .BI "int tickit_version_minor(void);" .BI "int tickit_version_patch(void);" .EE .sp .SH DESCRIPTION These three macros provide the version numbers of the development header at compiletime. The related functions return the version numbers from the running library. Between them they can be used to check at program startup that the library version is suitable to support the running program. .SH "RETURN VALUE" Each \fBtickit_version_*\fP() function returns a version integer. .SH "SEE ALSO" .BR tickit (7) libtickit-0.3.4/man/tickit_watch_cancel.30000644000000000000000000000134113613430267016447 0ustar 00000000000000.TH TICKIT_WATCH_CANCEL 3 .SH NAME tickit_watch_cancel \- remove a previously-installed callback .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_watch_cancel(Tickit *" t ", void *" watch ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_watch_cancel\fP() removes a callback previously registered, by the identifier pointer returned when it was added. If it had been added with the \fBTICKIT_BIND_UNBIND\fP flag then it will be invoked with the \fBTICKIT_EV_UNBIND\fP as it is removed. .SH "RETURN VALUE" \fBtickit_watch_cancel\fP() returns no value. .SH "SEE ALSO" .BR tickit_new_stdio (3), .BR tickit_watch_timer_after_msec (3), .BR tickit_watch_timer_at_epoch (3), .BR tickit_watch_later (3), .BR tickit (7) libtickit-0.3.4/man/tickit_watch_later.30000644000000000000000000000307413613430267016336 0ustar 00000000000000.TH TICKIT_WATCH_LATER 3 .SH NAME tickit_watch_later \- invoke a callback at the next round of IO events .SH SYNOPSIS .EX .B #include .sp .BI "typedef int " TickitCallbackFn "(Tickit *" t ", TickitEventflags " flags , .BI " void *" info ", void *" user ); .sp .BI "void *tickit_watch_later(Tickit *" t ", int " msec ", TickitBindFlags " flags , .BI " TickitCallbackFn *" fn ", void *" user ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_watch_later\fP() registers a callback function to be invoked by the toplevel event loop as it processes the next round of IO events. A registered callback will be invoked by a running call to \fBtickit_run\fP(3). The callback function will be invoked once, and then destroyed afterwards. The \fIinfo\fP pointer will be \fBNULL\fP. .PP When invoked, the callback function is always passed both \fBTICKIT_EV_FIRE\fP and \fBTICKIT_EV_UNBIND\fP flags to its \fIflags\fP argument. If the callback had been registered with the \fBTICKIT_BIND_DESTROY\fP flag, then it will also be invoked with the \fBTICKIT_EV_DESTROY\fP flag if it has not yet been invoked by the time the toplevel instance is destroyed. .PP If cancelled by \fBtickit_watch_cancel\fP(3) the callback function is invoked with just the \fBTICKIT_EV_UNBIND\fP flag if it had been registered with \fBTICKIT_BIND_UNBIND\fP. .SH "RETURN VALUE" \fBtickit_watch_later\fP() returns an opaque identifier pointer. .SH "SEE ALSO" .BR tickit_new_stdio (3), .BR tickit_watch_timer_after_msec (3), .BR tickit_watch_timer_at_epoch (3), .BR tickit_watch_cancel (3), .BR tickit (7) libtickit-0.3.4/man/tickit_watch_timer_after_msec.30000644000000000000000000000357413613430267020544 0ustar 00000000000000.TH TICKIT_WATCH_TIMER_AFTER_MSEC 3 .SH NAME tickit_watch_timer_after_* \- invoke a callback after a fixed delay .SH SYNOPSIS .EX .B #include .sp .BI "typedef int " TickitCallbackFn "(Tickit *" t ", TickitEventflags " flags , .BI " void *" info ", void *" user ); .sp .BI "void *tickit_watch_timer_after_msec(Tickit *" t ", int " msec , .BI " TickitBindFlags " flags ", TickitCallbackFn *" fn ", void *" user ); .BI "void *tickit_watch_timer_after_tv(Tickit *" t ", const struct timeval *" after , .BI " TickitBindFlags " flags ", TickitCallbackFn *" fn ", void *" user ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION The \fBtickit_watch_timer_after_*\fP() family of functions register a callback function to be invoked by the toplevel event loop a fixed delay afterwards. A registered callback will be invoked by a running call to \fBtickit_run\fP(3). The callback function will be invoked once, and then destroyed afterwards. Each function returns an opaque pointer value which serves to identify this instance. The \fIinfo\fP pointer will be \fBNULL\fP. .PP When invoked, the callback function is always passed both \fBTICKIT_EV_FIRE\fP and \fBTICKIT_EV_UNBIND\fP flags to its \fIflags\fP argument. If the callback had been registered with the \fBTICKIT_BIND_DESTROY\fP flag, then it will also be invoked with the \fBTICKIT_EV_DESTROY\fP flag if it has not yet been invoked by the time the toplevel instance is destroyed. .PP If cancelled by \fBtickit_watch_cancel\fP(3) the callback function is invoked with just the \fBTICKIT_EV_UNBIND\fP flag if it had been registered with \fBTICKIT_BIND_UNBIND\fP. .SH "RETURN VALUE" \fBtickit_watch_timer_after_msec\fP() and \fBtickit_watch_timer_after_tv\fP() return an opaque identifier pointer. .SH "SEE ALSO" .BR tickit_new_stdio (3), .BR tickit_watch_timer_at_epoch (3), .BR tickit_watch_later (3), .BR tickit_watch_cancel (3), .BR tickit (7) libtickit-0.3.4/man/tickit_watch_timer_at_epoch.30000644000000000000000000000356213613430267020213 0ustar 00000000000000.TH TICKIT_WATCH_TIMER_AT_EPOCH 3 .SH NAME tickit_watch_timer_at_* \- invoke a callback at a fixed future time .SH SYNOPSIS .EX .B #include .sp .BI "typedef int " TickitCallbackFn "(Tickit *" t ", TickitEventflags " flags , .BI " void *" info ", void *" user ); .sp .BI "void *tickit_watch_timer_at_epoch(Tickit *" t ", time_t " at , .BI " TickitBindFlags " flags ", TickitCallbackFn *" fn ", void *" user ); .BI "void *tickit_watch_timer_at_tv(Tickit *" t ", const struct timeval *" at , .BI " TickitBindFlags " flags ", TickitCallbackFn *" fn ", void *" user ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION The \fBtickit_watch_timer_at_*\fP() family of functions register a callback function to be invoked by the toplevel event loop at a fixed time in the future. A registered callback will be invoked by a running call to \fBtickit_run\fP(3). The callback function will be invoked once, and then destroyed afterwards. Each function returns an opaque pointer value which serves to identify this instance. The \fIinfo\fP pointer will be \fBNULL\fP. .PP When invoked, the callback function is always passed both \fBTICKIT_EV_FIRE\fP and \fBTICKIT_EV_UNBIND\fP flags to its \fIflags\fP argument. If the callback had been registered with the \fBTICKIT_BIND_DESTROY\fP flag, then it will also be invoked with the \fBTICKIT_EV_DESTROY\fP flag if it has not yet been invoked by the time the toplevel instance is destroyed. .PP If cancelled by \fBtickit_watch_cancel\fP(3) the callback function is invoked with just the \fBTICKIT_EV_UNBIND\fP flag if it had been registered with \fBTICKIT_BIND_UNBIND\fP. .SH "RETURN VALUE" \fBtickit_watch_timer_at_epoch\fP() and \fBtickit_watch_timer_at_tv\fP() return an opaque identifier pointer. .SH "SEE ALSO" .BR tickit_new_stdio (3), .BR tickit_watch_timer_after_msec (3), .BR tickit_watch_later (3), .BR tickit_watch_cancel (3), .BR tickit (7) libtickit-0.3.4/man/tickit_window.70000644000000000000000000002766313613430267015366 0ustar 00000000000000.TH TICKIT_WINDOW 7 .SH NAME TickitWindow \- a window for drawing operations and input .SH SYNOPSIS .EX .B #include .sp .BI "typedef struct " TickitWindow ; .EE .sp .SH DESCRIPTION A \fBTickitWindow\fP instance represents a rectangular region of the screen. Windows are constructed as sub-divisions of existing windows, ultimately coming from a special "root window" is that represents the entire area of the terminal. Each window allows drawing to its region of the screen by responding to an event that signifies damage to that area that needs to be repainted, and has other events that represent user input. .PP A window occupies a given size and position within its parent (apart from the root window, which occupies the entire terminal). Conceptually a window can be placed outside of the bounds of its parent, though any drawing output is clipped to the parent (and its parent, hierarchially, all the way to the root). A window may be hidden, at which point none of its output affects the screen, nor will it receive input events. .PP The child windows of a given parent form an ordered list, the order of which can be modified. Drawing operations on a window only take effect if there are no higher siblings that would obscure it. The stacking order also affects the way that windows receive mouse events, with higher-level window shielding lower ones from receiving an event at a given cell. .PP Each window tracks whether a single child window has the "input focus"; this is the child window that will be informed of keyboard input events. The innermost window following the focus chain gets the first chance to react to the event, followed by successively outer ones if it remains unhandled. .PP Newly-exposed areas of windows are tracked by the root window, ready to be rendered by expose events. The root window itself will expose new areas caused by terminal resizes, and the entire root window is entirely exposed initially, to allow the application to run its initial rendering on startup. Each window stores a \fBTickitPen\fP instance that can be used to apply a default style to any rendering operations that take place within it or its children. .SH FUNCTIONS A new top-level \fBTickitWindow\fP instance to represent the entire terminal is created by calling \fBtickit_get_rootwin\fP(3) on the toplevel \fBTickit\fP instance, further sub-divided into regions using \fBtickit_window_new\fP(3). A window instance stores a reference count to make it easier for applications to manage the lifetime of windows. A new window starts with a count of one, and it can be adjusted using \fBtickit_window_ref\fP(3) and \fBtickit_window_unref\fP(3). When the count reaches zero the instance is destroyed. A window can also be immediately removed from its parent with \fBtickit_window_close\fP(3). .PP The ancestry of a window can be queried using \fBtickit_window_parent\fP(3) and \fBtickit_window_root\fP(3). The stored child windows can be queried by using \fBtickit_window_children\fP(3) and \fBtickit_window_get_children\fP(3). The backing terminal can be queried with \fBtickit_window_get_term\fP(3). .PP The stacking order of a window among its siblings can be controlled using \fBtickit_window_raise\fP(3), \fBtickit_window_raise_to_front\fP(3), \fBtickit_window_lower\fP(3) and \fBtickit_window_lower_to_back\fP(3). Its visibility can be controlled using \fBtickit_window_show\fP(3) and \fBtickit_window_hide\fP(3), and queried using \fBtickit_window_is_visible\fP(3). .PP The position of a window within its parent can be queried using \fBtickit_window_get_geometry\fP(3) and within the terminal as a whole using \fBtickit_window_get_abs_geometry\fP(3). It can be resized using \fBtickit_window_resize\fP(3), moved using \fBtickit_window_reposition\fP(3), or both using \fBtickit_window_set_geometry\fP(3). .PP A window can be given the input focus using \fBtickit_window_take_focus\fP(3), and can be queried to see if it has the focus using \fBtickit_window_is_focused\fP(3). Windows normally only invoke focus events about themselves, but can be made to invoke events about children windows as well by using \fBtickit_window_set_focus_child_notify\fP(3). When a window has the input focus, the properties of the terminal cursor can be set using \fBtickit_window_set_cursor_position\fP(3), \fBtickit_window_set_cursor_visible\fP(3) and \fBtickit_window_set_cursor_shape\fP(3). .PP The \fBTickitPen\fP instance associated with each window for drawing purposes can be obtained using \fBtickit_window_get_pen\fP(3), and replaced using \fBtickit_window_set_pen\fP(3). This pen is used during expose events, which can be requested using \fBtickit_window_expose\fP(3). Pending expose events and other activity are performed by calling \fBtickit_window_flush\fP(3) on the root window instance. .PP While most drawing operations are performed in a deferred manner using expose events, scrolling of the terminal content can be directly requested using \fBtickit_window_scrollrect\fP(3), \fBtickit_window_scroll\fP(3) or \fBtickit_window_scroll_with_children\fP(3). .SH EVENTS A window instance stores a list of event handlers. Each event handler is associated with one event type and stores a function pointer, and an arbitrary pointer containing user data. Event handlers may be installed using \fBtickit_window_bind_event\fP(3) and removed using \fBtickit_window_unbind_event_id\fP(3). .PP The event types recognised are: .TP .B TICKIT_WINDOW_ON_DESTROY The window instance is being destroyed. .TP .B TICKIT_WINDOW_ON_KEY A key has been pressed on the keyboard while this window has input focus (or is set to steal input). \fIinfo\fP will point to a structure defined the same as the \fBTICKIT_EV_KEY\fP event described in \fBtickit_term\fP(7). .TP .B TICKIT_WINDOW_ON_MOUSE A mouse button has been pressed or released, the mouse cursor moved while dragging a button, or the wheel has been scrolled while the cursor is within the bounds of this window (or the window is set to steal input), or certain kinds of mouse dragging behaviour have happened. .sp \fIinfo\fP will point to a structure defined the same as the \fBTICKIT_EV_MOUSE\fP event described in \fBtickit_term\fP(7), except that the position given by the \fIline\fP and \fIcol\fP fields will be relative to the window, rather than the terminal as a whole. .sp In addition to the basic mouse events found at the terminal layer, there are a few additional kinds of events that occur during mouse dragging. These give information about mouse drag motions within a window or between different windows. .RS .TP .B TICKIT_MOUSEEV_DRAG_START A dragging motion has started. This event is delivered just before the \fBTICKIT_MOUSEEV_DRAG\fP event, and gives the original position of the mouse before it started dragging (i.e. the position of the press event). .TP .B TICKIT_MOUSEEV_DRAG_OUTSIDE A dragging motion that was started within this window has now moved outside it. In this case, the position given by the event will be somewhere outside the bounds of the window it is delivered to. This event is delivered directly to the source window; i.e. the window that handled the \fBTICKIT_MOUSEEV_DRAG_START\fP event. .TP .B TICKIT_MOUSEEV_DRAG_DROP A dragging motion has stopped by the mouse button being released. This event is delivered normally at the position of the mouse cursor. .TP .B TICKIT_MOUSEEV_DRAG_STOP A dragging motion has stopped by the mouse button being released. This event is delivered directly to the source window; i.e. the window that handled the \fBTICKIT_MOUSEEV_DRAG_START\fP event. If that is a different window than the one that received the \fBTICKIT_MOUSEEV_DRAG_STOP\fP event then the position may be outside the bounds of the window. .RE .TP .B TICKIT_WINDOW_ON_GEOMCHANGE At least one of the fields of the window geometry have been changed, meaning it now occupies a different area of the screen. \fIinfo\fP will point to a structure defined as: .sp .EX .B typedef struct { .BI " TickitRect " rect ; .BI " TickitRect " oldrect ; .BI "} " TickitGeomchangeEventInfo ; .EE .IP \fIrect\fP gives the new geometry of the window relative to its parent, and \fIoldrect\fP gives the previous geometry. .TP .B TICKIT_WINDOW_ON_EXPOSE An area of the window needs to be re-rendered because it has now been freshly exposed; either because of stacking or visibilty changes of this or sibling windows, a cascaded expose event on its parent, or due to a call to \fBtickit_window_expose\fP(). \fIinfo\fP will point to a structure defined as: .sp .EX .B typedef struct { .BI " TickitRect " rect ; .BI " TickitRenderBuffer *" rb ; .BI "} " TickitExposeEventInfo ; .EE .IP \fIrect\fP gives the region of the window that needs to be redrawn. This will always be inside the window's bounds. If multiple pending regions need to be exposed, they are output in non-overlapping segments. The handling function or functions should then use the \fBTickitRenderBuffer\fP instance given by the \fIrb\fP field to draw the required contents of the window to. This instance will already be set up with the appropriate drawing pen, clipping rectangle and hole regions to account for the window hierarchy. .TP .B TICKIT_WINDOW_ON_FOCUS This window has either gained or lost the input focus, or a child of it has an this window is set to also notify on that case by using \fBtickit_window_set_focus_child_notify\fP(). \fIinfo\fP will point to a structure defined as: .sp .EX .B typedef struct { .BI " TickitFocusEventType " type ; .BI " TickitWindow *" win ; .BI "} " TickitFocusEventInfo ; .EE .IP \fItype\fP takes onw of the values \fBTICKIT_FOCUSEV_IN\fP or \fBTICKIT_FOCUSEV_OUT\fP. \fIwin\fP will normally be the window that is invoking the event, except for the case of notifications about child windows, where it will indicate which child has changed focus. When a focus change happens, the window losing focus receives its \fBTICKIT_FOCUSEV_OUT\fP event before the window gaining it receives its \fBTICKIT_FOCUSEV_IN\fP. .SH CONTROLS A window instance has a number of runtime-configuration control options that affect its behaviour. These can be set using \fBtickit_window_setctl_int\fP(3), and queried using \fBtickit_window_getctl_int\fP(3). The individual controls have human-readable string names that can be obtained by \fBtickit_window_ctlname\fP(3) and searched by name using \fBtickit_window_lookup_ctl\fP(3). The type of a control option can be queried using \fBtickit_window_ctltype\fP(3). .PP The options are given in an enumeration called \fBTickitWindowCtl\fP. The following control values are recognised: .in .TP .B TICKIT_WINCTL_CURSORBLINK (bool) The value is a boolean indicating whether the terminal text cursor should blink while this window has the input focus. .TP .B TICKIT_WINCTL_CURSORSHAPE (int) The value is an integer from the \fBTickitCursorShape\fP enumeration indicating what shape the terminal's text cursor should be while this window has the input focus. Values are: .RS .TP .B TICKIT_CURSORSHAPE_BLOCK A solid block filling the entire cell. .TP .B TICKIT_CURSORSHAPE_UNDER An underline below the character. .TP .B TICKIT_CURSORSHAPE_LEFT_BAR A vertical bar to the left of the character. .RE .TP .B TICKIT_WINCTL_CURSORVIS (bool) The value is a boolean indicating whether the terminal text cursor should be visible while this window has the input focus. .TP .B TICKIT_WINCTL_FOCUS_CHILD_NOTIFY (bool) The value is a boolean indicating whether the window will receive \fBTICKIT_EV_FOCUS\fP events when its child windows change focus states (when true), or whether the only focus events it will receive are ones relating to itself directly (when false). .TP .B TICKIT_WINCTL_STEAL_INPUT (bool) The value is a boolean indicating whether the window will receive all key events on its parent first, while it is the front-most child of its parent, even before the sibling that actually has input focus receives them. Additionally, the window will receive all mouse events, even those outside of its geometry. This option is useful when implementing popup windows such as menu bars. .SH "SEE ALSO" .BR tickit (7), .BR tickit_term (7), .BR tickit_renderbuffer (7), .BR tickit_rect (7) libtickit-0.3.4/man/tickit_window_bind_event.30000644000000000000000000000277213613430267017551 0ustar 00000000000000.TH TICKIT_WINDOW_BIND_EVENT 3 .SH NAME tickit_window_bind_event, tickit_window_unbind_event_id \- add or remove event handlers .SH SYNOPSIS .EX .B #include .sp .BI "typedef int " TickitWindowEventFn "(TickitWindow *" win ", TickitEventFlags " flags , .BI " void *" info ", void *" user ); .sp .BI "int tickit_window_bind_event(TickitWindow *" win ", TickitWindowEvent " ev , .BI " TickitBindFlags " flags , .BI " TickitWindowEventFn *" fn ", void *" user ); .BI "void tickit_window_unbind_event_id(TickitWindow *" win ", int " id ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_bind_event\fP() adds a new event handler to the list of handlers stored by the window, and returns an integer to identify this handler. This handler will be invoked for occurances of the event given by the \fIev\fP argument. When invoked, \fIfunc\fP will be passed the window instance, a flags bitmask, a pointer to an event information structure whose type depends on the event, and the user data pointer it was installed with. .PP \fBtickit_window_unbind_event_id\fP() removes an event handler previously added, by the identifier returned when it was added, invoking it with the \fBTICKIT_EV_UNBIND\fP flag if it was installed with \fBTICKIT_BIND_UNBIND\fP. .SH "RETURN VALUE" \fBtickit_window_bind_event\fP() returns an identifier integer. \fBtickit_window_unbind_event_id\fP() returns no value. .SH "SEE ALSO" .BR tickit_window_new (3), .BR tickit_window_flush (3), .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_children.30000644000000000000000000000176613613430267017226 0ustar 00000000000000.TH TICKIT_WINDOW_CHILDREN 3 .SH NAME tickit_window_children, tickit_window_get_children \- obtain child windows from a window .SH SYNOPSIS .EX .B #include .sp .BI "size_t tickit_window_children(const TickitWindow *" win ); .BI "size_t tickit_window_get_children(const TickitWindow *" win , .BI " TickitWindow *" children "[], size_t " n ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_children\fP() returns the number of immediate child windows the given window has. .PP \fBtickit_window_get_children\fP() copies at most \fIn\fP child window pointers into the array given by \fBchildren\fP, and returns the number of pointers it copied (which may be fewer than the total stored, if the array was of insufficient size). .SH "RETURN VALUE" \fBtickit_window_children\fP() and \fBtickit_window_get_children\fP() return integers giving the number of entries operated on. .SH "SEE ALSO" .BR tickit_window_new (3), .BR tickit_window_parent (3), .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_close.30000644000000000000000000000126413613430267016534 0ustar 00000000000000.TH TICKIT_WINDOW_CLOSE 3 .SH NAME tickit_window_close \- remove a window from the tree .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_window_close(TickitWindow *" win ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_close\fP() removes a window from its parent, causing it to no longer affect rendering or recieve input events in any way. After this is done the only operation that is defined any more is \fBtickit_window_unref\fP(3). The window may not be used for further drawing. .SH "RETURN VALUE" \fBtickit_window_close\fP() returns no value. .SH "SEE ALSO" .BR tickit_window_new (3), .BR tickit_window_hide (3), .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_ctlname.30000644000000000000000000000275413613430267017057 0ustar 00000000000000.TH TICKIT_WINDOW_CTLNAME 3 .SH NAME tickit_window_ctlname \- return the name of a window control .SH SYNOPSIS .EX .B #include .sp .BI "const char * tickit_window_ctlname(TickitWindowCtl " ctl ); .BI "TickitWindowCtl tickit_window_lookup_ctl(const char * " name ); .sp .BI "TickitType tickit_window_ctltype(TickitWindowCtl " ctl ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_ctlname\fP() returns the name of the given window control. These names are the lowercase strings given in \fBtickit_window\fP(7). .PP \fBtickit_window_lookup_ctl\fP() returns the control constant for a given name. If the name is not recognised then -1 is returned. .PP \fBtickit_window_ctltype\fP() returns the type of the given window control, as one of the following constants: .in .TP .B TICKIT_TYPE_BOOL The value is an integer representing a simple boolean choice, where any nonzero value is true. Use \fBtickit_window_setctl_int\fP(3) and \fBtickit_window_getctl_int\fP(3). .TP .B TICKIT_TYPE_INT The value is an integer representing a quantity, or a value in some particular enumeration. Use \fBtickit_window_setctl_int\fP(3) and \fBtickit_window_getctl_int\fP(3). .PP For unrecognised control types, the function returns 0. .SH "RETURN VALUE" \fBtickit_window_ctlname\fP() returns a constant string pointer. \fBtickit_window_lookup_ctl\fP() returns an control constant or -1. \fBtickit_window_ctltype\fP() returns a control type constant or 0. .SH "SEE ALSO" .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_destroy.30000644000000000000000000000133613613430267017120 0ustar 00000000000000.TH TICKIT_WINDOW_DESTROY 3 .SH NAME tickit_window_destroy \- destroy a window instance .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_window_destroy(TickitWindow *" win ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_destroy\fP() destroys the given window instance and releases any resources controlled by it, recursively destroying any child windows of the given one as well. It will unbind all the bound event handlers, causing \fBTICKIT_EV_UNBIND\fP events if appropriate. .SH "RETURN VALUE" \fBtickit_window_destroy\fP() returns no value. .SH "SEE ALSO" .BR tickit_window_new (3), .BR tickit_window_bind_event (3), .BR tickit_window_unbind_event_id (3), .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_expose.30000644000000000000000000000224513613430267016732 0ustar 00000000000000.TH TICKIT_WINDOW_EXPOSE 3 .SH NAME tickit_window_expose \- mark an area of a window for re-rendering .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_window_expose(TickitWindow *" win ", const TickitRect *" exposed ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_expose\fP() marks the given area of the given window as needing to be re-rendered, causing it to receive a \fBTICKIT_EV_EXPOSE\fP event when \fBtickit_window_flush\fP(3) is next called. \fIexposed\fP may be \fBNULL\fP, indicating that the entire window should be exposed. .PP If the window, or any of its parents, are hidden, then this function has no effect. Otherwise, it enqueues the corresponding area on the root window as being damaged, causing an \fBTICKIT_EV_EXPOSE\fP event to propagate upwards from the root the next time \fBtickit_window_flush\fP(3) is called. This will propagate up to any window occupying that area, meaning that this window or others may receive it. .SH "RETURN VALUE" \fBtickit_window_expose\fP() returns no value. .SH "SEE ALSO" .BR tickit_window_new (3), .BR tickit_window_flush (3), .BR tickit_window_bind_event (3), .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_flush.30000644000000000000000000000342113613430267016545 0ustar 00000000000000.TH TICKIT_WINDOW_FLUSH 3 .SH NAME tickit_window_flush \- invoke pending events on the window hierarchy .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_window_flush(TickitWindow *" win ); .EE .sp Link with \fI\-ltickit\fP. .SH DEPRECATED This function is deprecated and should not be used in new programs. Instead, obtain the root window from the toplevel \fBTickit\fP instance by calling \fBtickit_get_rootwin\fP(3), and its pending events will automatically be managed by the toplevel instance \fBtickit_run\fP(3) function. .SH DESCRIPTION \fBtickit_window_flush\fP() causes any pending activity in the window hierarchy to be performed. First it makes any window ordering changes that have been queued by \fBtickit_window_raise\fP(3) and \fBtickit_window_lower\fP(3), then fires any \fBTICKIT_EV_EXPOSE\fP events to render newly-exposed areas, before finally resetting the terminal cursor to the state required by whichever window has input focus. This function must be invoked on the root window instance. .PP An application working at the window level would typically use this function in conjunction with input even waiting, to drive the main loop of the core logic. Such a loop may look like: .sp .EX .in while(1) { tickit_window_flush(rootwin); tickit_term_input_wait_msec(term, -1); } .EE .PP There is no adverse effect of calling this function when here are no pending events on the window instance. An application that makes use of multiple root windows across multiple terminal instances in a multi-head setup can safely invoke it on all the root windows at once. .SH "RETURN VALUE" \fBtickit_window_flush\fP() returns no value. .SH "SEE ALSO" .BR tickit_window_new (3), .BR tickit_window_expose (3), .BR tickit_window_bind_event (3), .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_get_abs_geometry.30000644000000000000000000000150413613430267020743 0ustar 00000000000000.TH TICKIT_WINDOW_GET_ABS_GEOMETRY 3 .SH NAME tickit_window_get_abs_geometry \- query the absolute position of a window .SH SYNOPSIS .EX .B #include .sp .BI "TickitRect tickit_window_get_abs_geometry(const TickitWindow *" win ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_get_abs_geometry\fP() returns a \fBTickitRect\fP structure containing the absolute position of the given window within the underlying terminal. For a root window or an immediate child of it, this will be the same as its position given by \fBtickit_window_get_geometry\fP(3). For further-nested children, this will take account of the position of its parents. .SH "RETURN VALUE" .SH "SEE ALSO" .BR tickit_window_new (3), .BR tickit_window_get_geometry (3), .BR tickit_window_reposition (3), .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_get_geometry.30000644000000000000000000000300013613430267020107 0ustar 00000000000000.TH TICKIT_WINDOW_GET_GEOMETRY 3 .SH NAME tickit_window_get_geometry \- query the size and position of a window .SH SYNOPSIS .EX .B #include .sp .BI "TickitRect tickit_window_get_geometry(const TickitWindow *" win ); .sp .BI "int tickit_window_top(const TickitWindow *" win ); .BI "int tickit_window_left(const TickitWindow *" win ); .BI "int tickit_window_lines(const TickitWindow *" win ); .BI "int tickit_window_cols(const TickitWindow *" win ); .sp .BI "int tickit_window_bottom(const TickitWindow *" win ); .BI "int tickit_window_right(const TickitWindow *" win ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_get_geometry\fP() returns the size and position within its immediate parent of the given window. When invoked on a root window, its top left corner will be at zero, and its size will give the size of the underlying terminal. .PP The four convenience accessors return the four fields of the rectangle structure immediately. They may be implemented as macros. .PP \fBtickit_window_bottom\fP() gives the line number in the parent where the window stops (i.e. the first line after its content). \fBtickit_window_right\fP() gives the column number in the parent where the window stops. .SH "RETURN VALUE" \fBtickit_window_get_geometry\fP() returns a \fBTickitRect\fP structure. The other functions return integers. .SH "SEE ALSO" .BR tickit_window_new (3), .BR tickit_window_get_abs_geometry (3), .BR tickit_window_set_geometry (3), .BR tickit_window (7), .BR tickit_rect (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_get_pen.30000644000000000000000000000250613613430267017050 0ustar 00000000000000.TH TICKIT_WINDOW_GET_PEN 3 .SH NAME tickit_window_get_pen, tickit_window_set_pen \- manipulate the window's rendering pen .SH SYNOPSIS .EX .B #include .sp .BI "TickitPen *tickit_window_get_pen(const TickitWindow *" win ); .BI "void tickit_window_set_pen(TickitWindow *" win ", TickitPen " pen ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_get_pen\fP() returns a pointer to the given window's pen instance. This pen instance is directly stored by the window. The application is permitted to modify this pen instance, though any modifications will not be observed until the window is next exposed for rendering. .PP \fBtickit_window_set_pen\fP() stores a new pointer for a pen instance into the given window. The window retains a reference to that pen and increments the reference count on it. Any previous pen is unreferenced first. .PP Because the window stores what may be one of many references to the pen instance, applications that do modify the pen should be careful that such modifications do not adversely affect other users of the same pen. .SH "RETURN VALUE" \fBtickit_window_get_pen\fP() returns a pointer to a pen instance. \fBtickit_window_set_pen\fP() returns no value. .SH "SEE ALSO" .BR tickit_window_new (3), .BR tickit_window_expose (3), .BR tickit_window (7), .BR tickit_pen (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_get_term.30000644000000000000000000000121113613430267017225 0ustar 00000000000000.TH TICKIT_WINDOW_GET_TERM 3 .SH NAME tickit_window_get_term \- query the backing terminal of a window .SH SYNOPSIS .EX .B #include .sp .BI "TickitTerm *tickit_window_get_term(const TickitWindow *" win ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_get_term\fP() returns the backing terminal underlying a given window. This is the terminal instance that the window's root window was originally created using. .SH "RETURN VALUE" \fBtickit_window_get_term\fP() returns a pointer to a terminal instance. .SH "SEE ALSO" .BR tickit_window_new_root (3), .BR tickit_window_root (3), .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_new.30000644000000000000000000000454313613430267016223 0ustar 00000000000000.TH TICKIT_WINDOW_NEW 3 .SH NAME tickit_window_new \- create a new sub-window .SH SYNOPSIS .EX .B #include .sp .BI "TickitWindow *tickit_window_new(TickitWindow *" parent ", TickitRect " rect , .BI " TickitWindowFlags " flags ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_new\fP() creates a new sub-window as a child of the given parent, using the given rectangle as its initial geometry relative to its parent. The \fIflags\fP argument is a bitmask of flags influencing the initial behaviour of the child window. .in .TP .B TICKIT_WINDOW_HIDDEN The new child window will be created in the hidden state initially. No pending expose event will occur because of it. It can later be shown by calling \fBtickit_window_show\fP(3). .TP .B TICKIT_WINDOW_LOWEST Normally the most recently-added child window is ordered at the top of the list in its parent, making it appear front-most in the display. This flag causes it to be added at the bottom of the list, appearing lowest-most instead. .TP .B TICKIT_WINDOW_ROOT_PARENT The new child window will have the root window of the window tree as its parent, instead of the given window. The position will be automatically adjusted so that the new window still appears at the given geometry position relative to the window passed as the \fIparent\fP argument. .TP .B TICKIT_WINDOW_STEAL_INPUT The new child window will initially be set to steal input events; see \fBtickit_window_set_steal_input\fP(3). .IP This flag is not useful when combined with \fBTICKIT_WINDOW_LOWEST\fP, as the input-stealing behaviour only applies while the window is front-most within its parent. .TP .B TICKIT_WINDOW_POPUP A convenient shortcut to specifying both \fBTICKIT_WINDOW_ROOT_PARENT\fP and \fBTICKIT_WINDOW_STEAL_INPUT\fP flags. This is useful for implementing popup dialog windows, menus, and other such UI behaviours. .PP The reference count of a newly-constructed window instance will be one. This can be incremented or decremented using \fBtickit_window_ref\fP(3) and \fBtickit_window_unref\fP(3). When its reference count reaches zero it is destroyed. .SH "RETURN VALUE" If successful, \fBtickit_window_new\fP() returns a pointer to the new instance. .SH "SEE ALSO" .BR tickit_window_new_root (3), .BR tickit_window_bind_event (3), .BR tickit_window_expose (3), .BR tickit_window_close (3), .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_new_root.30000644000000000000000000000311013613430267017253 0ustar 00000000000000.TH TICKIT_WINDOW_NEW_ROOT 3 .SH NAME tickit_window_new_root \- create a new toplevel root window .SH SYNOPSIS .EX .B #include .sp .BI "TickitWindow *tickit_window_new_root(TickitTerm *" term ); .EE .sp Link with \fI\-ltickit\fP. .SH DEPRECATED This function is deprecated and should not be used in new programs. Instead, obtain the root window from the toplevel \fBTickit\fP instance by calling \fBtickit_get_rootwin\fP(3). .SH DESCRIPTION \fBtickit_window_new_root\fP() creates a new toplevel \fBTickitWindow\fP instance to represent the drawing area on the given terminal instance. The terminal instance is stored by the window itself, and is used for flushing the \fBTickitRenderBuffer\fP to when expose events occur. The root window will increase the reference count of its backing terminal by one; this reference will be dropped when the window is destroyed. .PP The root window behaves similar to other windows within the application, with the following differences: .IP * Its position remains fixed at zero, and should not be set by the application. .IP * Its size is managed automatically by observing resize events from the underlying terminal instance, and should not be set by the application. .IP * It is initially fully-damaged after construction, and will have a pending expose event waiting to occur. .SH "RETURN VALUE" If successful, \fBtickit_window_new_root\fP() returns a pointer to the new instance. .SH "SEE ALSO" .BR tickit_window_new (3), .BR tickit_window_get_term (3), .BR tickit_window_bind_event (3), .BR tickit_window_expose (3), .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_parent.30000644000000000000000000000157213613430267016722 0ustar 00000000000000.TH TICKIT_WINDOW_PARENT 3 .SH NAME tickit_window_parent, tickit_window_root \- query the ancestry of a window .SH SYNOPSIS .EX .B #include .sp .BI "TickitWindow *tickit_window_parent(const TickitWindow *" win ); .BI "TickitWindow *tickit_window_root(const TickitWindow *" win ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_parent\fP() returns a pointer to the given window's immediate parent instance (i.e. the one it was created from), or \fBNULL\fP if called on a root window instance. .PP \fBtickit_window_root\fP() returns a pointer to the given window's ultimate root window instance. This may be the window itself, if called on a root window instance. .SH "RETURN VALUE" \fBtickit_window_parent\fP() and \fBtickit_window_root\fP() return pointers to window instances. .SH "SEE ALSO" .BR tickit_window_new (3), .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_raise.30000644000000000000000000000271513613430267016534 0ustar 00000000000000.TH TICKIT_WINDOW_RAISE 3 .SH NAME tickit_window_raise, tickit_window_lower \- change window stacking order .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_window_raise(TickitWindow *" win ); .BI "void tickit_window_raise_to_front(TickitWindow *" win ); .BI "void tickit_window_lower(TickitWindow *" win ); .BI "void tickit_window_lower_to_back(TickitWindow *" win ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_raise\fP() brings the given window one level higher up in the stacking order in its parent. \fBtickit_window_raise_to_front\fP() brings the given window to the front of the stacking order in its parent. These functions may cause new areas of the given window or children of it to become exposed, and may result in \fBTICKIT_EV_EXPOSE\fP events when \fBtickit_window_flush\fP(3) is next called. .PP \fBtickit_window_lower\fP() brings the given window one level lower down in the stacking order in its parent. \fBtickit_window_lower_to_back\fP() brings the given window to the back of the stacking order in its parent. These functions may cause new areas of other sibling windows or their children to become exposed, and may result in \fBTICKIT_EV_EXPOSE\fP events when \fBtickit_window_flush\fP(3) is next called. .SH "RETURN VALUE" These functions return no value. .SH "SEE ALSO" .BR tickit_window_new (3), .BR tickit_window_show (3), .BR tickit_window_bind_event (3), .BR tickit_window_flush (3), .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_ref.30000644000000000000000000000177213613430267016207 0ustar 00000000000000.TH TICKIT_WINDOW_REF 3 .SH NAME tickit_window_ref, tickit_window_unref \- adjust the refcount of a window .SH SYNOPSIS .EX .B #include .sp .BI "TickitWindow *tickit_window_ref(TickitWindow *" win ); .BI "void tickit_window_unref(TickitWindow *" win ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_ref\fP() increments the stored refcount of the given window instance by one. It returns the pointer argument itself, so it is useful for chaining. .PP \fBtickit_window_unref\fP() decrements the stored refcount of the given window instance by one. If the refcount drops to zero, the instance is destroyed. This will release any resources controlled by it and unbind all the bound event handlers, causing handlers to be invoked with the \fBTICKIT_EV_DESTROY\fP flag if appropriate. .SH "RETURN VALUE" \fBtickit_window_ref\fP() returns a window instance pointer. \fBtickit_window_unref\fP() returns no value. .SH "SEE ALSO" .BR tickit_window_new (3), .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_scroll.30000644000000000000000000000401713613430267016724 0ustar 00000000000000.TH TICKIT_WINDOW_SCROLL 3 .SH NAME tickit_window_scroll, tickit_window_scrollrect \- scroll the area of a window .SH SYNOPSIS .EX .B #include .sp .BI "bool tickit_window_scroll(TickitWindow *" win ", int " downward ", int " rightward ); .BI "bool tickit_window_scrollrect(TickitWindow *" win ", const TickitRect *" rect , .BI " int " downward ", int " rightward ", TickitPen *" pen ); .sp .BI "bool tickit_window_scroll_with_children(TickitWindow *" win , .BI " int " downward ", int " rightward ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_scroll\fP() requests that the underlying terminal scroll the contents of the area corresponding to the visible region (or regions) of the given window by the given amount. This takes into account aspects of the visiblity of the window, such as being obscured by siblings or siblings of ancestors, child windows, or whether the window or any parent is hidden. It returns \fBtrue\fP if it managed to perform the required scrolling, or \fBfalse\fP if not. .PP \fBtickit_window_scrollrect\fP() requests that the terminal scroll the contents of the area corresponding to the given rectangle within the window, and otherwise works analogously to \fBtickit_window_scroll\fP(). If the \fIpen\fP argument is non-\fBNULL\fP then it is applied to the terminal before any erase operations within the window are performed, allowing a different background colour if set. .PP \fBtickit_window_scroll_with_children\fP() is similar again, except that it does not consider obscuring regions by child windows; only siblings or siblings of ancestors. This is intended for scrolling a container of windows, which will move all of the sub-windows too. Note that this function does not actually move the child windows, it simply requests a scrolling operation on the underlying terminal. .SH "RETURN VALUE" These functions all return a boolean. .SH "SEE ALSO" .BR tickit_window_new (3), .BR tickit_window_set_geometry (3), .BR tickit_window_expose (3), .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_set_cursor_position.30000644000000000000000000000255713613430267021551 0ustar 00000000000000.TH TICKIT_WINDOW_SET_CURSOR_POSITION 3 .SH NAME tickit_window_set_cursor_position, tickit_window_set_cursor_visible, tickit_window_set_cursor_shape \- modify the cursor state on a window .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_window_set_cursor_position(TickitWindow *" win ", int " line ", int " col ); .BI "void tickit_window_set_cursor_visible(TickitWindow *" win ", bool " visible ); .BI "void tickit_window_set_cursor_shape(TickitWindow *" win ", TickitCursorShape " shape ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_set_cursor_position\fP() sets the position within the given window where the terminal cursor would be displayed, if the window has the focus. .PP \fBtickit_window_set_cursor_visible\fP() sets whether the terminal cursor is visible when the given window has the focus. .PP \fBtickit_window_set_cursor_shape\fP() sets what shape the terminal cursor will have if the given window has the focus. .PP Note that none of these functions actually affect the input focus directly, they simply set what the state of the cursor will be if the given window has the input focus. To set the input focus, see \fBtickit_window_take_focus\fP(3). .SH "RETURN VALUE" These functions all return no value. .SH "SEE ALSO" .BR tickit_window_new (3), .BR tickit_window_take_focus (3), .BR tickit_window (7), .BR tickit_rect (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_set_focus_child_notify.30000644000000000000000000000206513613430267022154 0ustar 00000000000000.TH TICKIT_WINDOW_SET_FOCUS_CHILD_NOTIFY 3 .SH NAME tickit_window_set_focus_child_notify \- control whether windows are informed of child focus changes .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_window_set_focus_child_notify(TickitWindow *" win ", bool " notify ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_set_focus_child_notify\fP() sets the value of a setting on the given window which controls whether the window will receive \fBTICKIT_EV_FOCUS\fP events when its child windows change focus state (when true), or whether the only focus events it receives are ones relating to itself directly (when false). .PP This function is discouraged in favour of using the \fBTICKIT_WINCTL_FOCUS_CHILD_NOTIFY\fP window control with \fBtickit_window_setctl_int\fP(3). .SH "RETURN VALUE" \fBtickit_window_set_focus_child_notify\fP() returns no value. .SH "SEE ALSO" .BR tickit_window_new (3), .BR tickit_window_take_focus (3), .BR tickit_window_set_cursor_position (3), .BR tickit_window_bind_event (3), .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_set_geometry.30000644000000000000000000000313613613430267020135 0ustar 00000000000000.TH TICKIT_WINDOW_SET_GEOMETRY 3 .SH NAME tickit_window_set_geometry \- set the size and position of a window .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_window_set_geometry(TickitWindow *" win ", TickitRect " geom ); .sp .BI "void tickit_window_resize(TickitWindow *" win ", int " lines ", int " cols ); .BI "void tickit_window_reposition(TickitWindow * " win ", int " top ", int " left ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_set_geometry\fP() sets the size and position of the given window to the details given in the rectangle structure. The position is relative to the window's immediate parent. The window does not have to be entirely contained within the parent; it is allowed to overlap any of the four edges, or even sit entirely outside of the parent. Content rendering and input events will be clipped to the bounds of its parent (and thus any ancestors up to the root) in any case. .PP This function causes a \fBTICKIT_EV_GEOMCHANGE\fP event to occur at the next tick, if the geometry was actually changed. If none of the four fields are modified then no event occurs; thus it is safe to call it optimistically to update the required size in case it has changed. .PP \fBtickit_window_resize\fP() and \fBtickit_window_reposition\fP() are shortcut functions that take two integer arguments directly for changing just the size, or just the origin position of the window. .SH "RETURN VALUE" These functions return no value. .SH "SEE ALSO" .BR tickit_window_new (3), .BR tickit_window_get_geometry (3), .BR tickit_window_bind_event (3), .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_set_steal_input.30000644000000000000000000000270413613430267020631 0ustar 00000000000000.TH TICKIT_WINDOW_SET_STEAL_INPUT 3 .SH NAME tickit_window_set_steal_input \- control whether windows steal all input events .SH SYNOPSIS .EX .B #include .sp .BI "bool tickit_window_is_steal_input(const TickitWindow *" win ); .BI "void tickit_window_set_steal_input(TickitWindow *" win ", bool " steal ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_is_steal_input\fP() returns the current state of the input-stealing setting on the window. If true, this causes the window to have special behaviour on input events that are likely to be useful to implement popup windows. Specifically, the window receives all key events on its parent even before the sibling with the input focus receives them, provided it is the front-most child of its parent. Additionally, the window will receive all mouse events, even those outside of its geometry. .PP \fBtickit_window_set_steal_input\fP() changes the value of this setting. .PP The initial value is normally false, unless the \fBTICKIT_WINDOW_STEAL_INPUT\fP flag was passed to the \fBtickit_window_new\fP(3) function call that created the window. .PP This function is discouraged in favour of using the \fBTICKIT_WINCTL_STEAL_INPUT\fP window control with \fBtickit_window_setctl_int\fP(3). .SH "RETURN VALUE" \fBtickit_window_is_steal_input\fP() returns a boolean. \fBtickit_window_set_steal_input\fP() returns no value. .SH "SEE ALSO" .BR tickit_window_new (3), .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_setctl_int.30000644000000000000000000000206613613430267017600 0ustar 00000000000000.TH TICKIT_WINDOW_SETCTL_INT 3 .SH NAME tickit_window_setctl_int\- set an integer window control .SH SYNOPSIS .EX .B #include .sp .BI "bool tickit_window_getctl_int(TickitWindow *" tt ", TickitWindowCtl " ctl ", int *" value ); .BI "bool tickit_window_setctl_int(TickitWindow *" tt ", TickitWindowCtl " ctl ", int " value ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_setctl_int\fP() performs a window control operation, setting the value of a numeric window control option. \fBtickit_window_getctl_int\fP() obtains the value of a window control setting. .PP For the list of possible \fBTickitWindowCtl\fP values and their meanings, see \fBtickit_window\fP(7). .SH "RETURN VALUE" \fBtickit_window_getctl_int\fP() returns a true value if it recognised the requested control and managed to return the current value of it; false if not. \fBtickit_window_setctl_int\fP() returns a true value if it recognised the requested control and managed to request the window to change it; false if not. .SH "SEE ALSO" .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_show.30000644000000000000000000000260313613430267016405 0ustar 00000000000000.TH TICKIT_WINDOW_SHOW 3 .SH NAME tickit_window_show, tickit_window_hide \- control a window's visibility .SH SYNOPSIS .EX .BI "void tickit_window_show(TickitWindow *" win ); .BI "void tickit_window_hide(TickitWindow *" win ); .sp .BI "bool tickit_window_is_visible(TickitWindow *" win ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_show\fP() makes the given window visible, allowing it to receive events and obscure lower sibling windows. This may cause it to receive \fBTICKIT_EV_EXPOSE\fP events when \fBtickit_window_flush\fP(3) is next called. .PP \fBtickit_window_hide\fP() makes the given window non-visible, preventing it from receiving events and making it transparent to lower sibling windows, so their content is now visible. This may cause lower siblings to receive \fBTICKIT_EV_EXPOSE\fP events when \fBtickit_window_flush\fP(3) is next called. .PP \fBtickit_window_is_visible\fP() tests whether the given window is visible within its immediate parent. This does not imply that it is actually visible to the terminal, as it may still have a hidden parent. .SH "RETURN VALUE" \fBtickit_window_show\fP() and \fBtickit_window_hide\fP() return no value. \fBtickit_window_is_visible\fP() returns a boolean. .SH "SEE ALSO" .BR tickit_window_new (3), .BR tickit_window_raise (3), .BR tickit_window_bind_event (3), .BR tickit_window_flush (3), .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/man/tickit_window_take_focus.30000644000000000000000000000215013613430267017545 0ustar 00000000000000.TH TICKIT_WINDOW_TAKE_FOCUS 3 .SH NAME tickit_window_take_focus, tickit_window_is_focused \- control the input focus .SH SYNOPSIS .EX .B #include .sp .BI "void tickit_window_take_focus(TickitWindow *" win ); .BI "bool tickit_window_is_focused(const TickitWindow *" win ); .EE .sp Link with \fI\-ltickit\fP. .SH DESCRIPTION \fBtickit_window_take_focus\fP() requests that the given window take the input focus, causing respectively the parent to take focus, recursively to the root. The terminal cursor will then take the properties defined by the given window. This results in a \fBTICKIT_EV_FOCUS\fP event being delivered to the previous owner of the focus, as well as to the given window. .PP \fBtickit_window_is_focused\fP() returns true if the given window currently has the input focus. .SH "RETURN VALUE" \fBtickit_window_take_focus\fP() returns no value. \fBtickit_window_is_focused\fP() returns a boolean. .SH "SEE ALSO" .BR tickit_window_new (3), .BR tickit_window_set_cursor_position (3), .BR tickit_window_set_focus_child_notify (3), .BR tickit_window_bind_event (3), .BR tickit_window (7), .BR tickit (7) libtickit-0.3.4/src/bindings.c0000644000000000000000000000770413613430267014367 0ustar 00000000000000#include "bindings.h" #include struct TickitBinding { struct TickitBinding *next; int id; int evindex; TickitBindFlags flags; TickitEventFn *fn; void *data; }; #define BINDING_ID_TOMBSTONE -1 static void cleanup(struct TickitBindings *bindings) { for(struct TickitBinding **bindp = &bindings->first; *bindp; /**/) { struct TickitBinding *bind = *bindp; if(bind->id != BINDING_ID_TOMBSTONE) { bindp = &(*bindp)->next; continue; } *bindp = bind->next; bind->next = NULL; free(bind); } bindings->needs_delete = false; } void tickit_bindings_run_event(struct TickitBindings *bindings, void *owner, int evindex, void *info) { int was_iterating = bindings->is_iterating; bindings->is_iterating = true; for(struct TickitBinding *bind = bindings->first; bind; bind = bind->next) if(bind->evindex == evindex) (*bind->fn)(owner, TICKIT_EV_FIRE, info, bind->data); bindings->is_iterating = was_iterating; if(!was_iterating && bindings->needs_delete) cleanup(bindings); } int tickit_bindings_run_event_whilefalse(struct TickitBindings *bindings, void *owner, int evindex, void *info) { int was_iterating = bindings->is_iterating; bindings->is_iterating = true; int ret = 0; for(struct TickitBinding *bind = bindings->first; bind; bind = bind->next) if(bind->evindex == evindex) { ret = (*bind->fn)(owner, TICKIT_EV_FIRE, info, bind->data); if(ret) goto exit; } exit: bindings->is_iterating = was_iterating; if(!was_iterating && bindings->needs_delete) cleanup(bindings); return ret; } int tickit_bindings_bind_event(struct TickitBindings *bindings, void *owner, int evindex, TickitBindFlags flags, TickitEventFn *fn, void *data) { int max_id = 0; struct TickitBinding **newp = &bindings->first; struct TickitBinding *next = NULL; if(flags & TICKIT_BIND_FIRST) { next = bindings->first; for(struct TickitBinding *bind = *newp; bind; bind = bind->next) if(bind->id > max_id) max_id = bind->id; } else { for(; *newp; newp = &(*newp)->next) if((*newp)->id > max_id) max_id = (*newp)->id; } *newp = malloc(sizeof(struct TickitBinding)); // TODO: malloc failure (*newp)->next = next; (*newp)->evindex = evindex; (*newp)->flags = flags & (TICKIT_BIND_UNBIND|TICKIT_BIND_DESTROY); (*newp)->fn = fn; (*newp)->data = data; return (*newp)->id = max_id + 1; } void tickit_bindings_unbind_event_id(struct TickitBindings *bindings, void *owner, int id) { for(struct TickitBinding **bindp = &bindings->first; *bindp; ) { struct TickitBinding *bind = *bindp; if(bind->id != id) { bindp = &(bind->next); continue; } if(bind->flags & TICKIT_EV_UNBIND) (*bind->fn)(owner, TICKIT_EV_UNBIND, NULL, bind->data); // zero out the structure bind->evindex = -1; bind->fn = NULL; if(!bindings->is_iterating) { *bindp = bind->next; bind->next = NULL; free(bind); /* no bindp update */ } else { bindings->needs_delete = true; bind->id = BINDING_ID_TOMBSTONE; bindp = &(bind->next); } } } void tickit_bindings_unbind_and_destroy(struct TickitBindings *bindings, void *owner) { /* TICKIT_EV_DESTROY events need to run in reverse order. Since the bindings * is singly-linked it is easiest just to reverse it then iterate. */ struct TickitBinding *revbinds = NULL; for(struct TickitBinding *bind = bindings->first; bind; /**/) { struct TickitBinding *this = bind; bind = bind->next; this->next = revbinds; revbinds = this; } for(struct TickitBinding *bind = revbinds; bind;) { struct TickitBinding *next = bind->next; if(bind->evindex == 0 || bind->flags & (TICKIT_EV_UNBIND|TICKIT_EV_DESTROY)) (*bind->fn)(owner, TICKIT_EV_UNBIND|TICKIT_EV_DESTROY, NULL, bind->data); free(bind); bind = next; } } libtickit-0.3.4/src/bindings.h0000644000000000000000000000535513613430267014374 0ustar 00000000000000#include "tickit.h" struct TickitBinding; struct TickitBindings { struct TickitBinding *first; int is_iterating : 1; int needs_delete : 1; }; typedef int TickitEventFn(void *owner, TickitEventFlags flags, void *info, void *data); void tickit_bindings_run_event(struct TickitBindings *bindings, void *owner, int evindex, void *info); int tickit_bindings_run_event_whilefalse(struct TickitBindings *bindings, void *owner, int evindex, void *info); int tickit_bindings_bind_event(struct TickitBindings *bindings, void *owner, int evindex, TickitBindFlags flags, TickitEventFn *fn, void *data); void tickit_bindings_unbind_event_id(struct TickitBindings *bindings, void *owner, int id); void tickit_bindings_unbind_and_destroy(struct TickitBindings *bindings, void *owner); #define DEFINE_BINDINGS_FUNCS(NAME,OWNER,EVENTFN) \ int tickit_##NAME##_bind_event(OWNER *owner, OWNER##Event evindex, \ TickitBindFlags flags, EVENTFN *fn, void *data) \ { \ return tickit_bindings_bind_event(&owner->bindings, owner, \ evindex, flags, (TickitEventFn*)fn, data); \ } \ \ void tickit_##NAME##_unbind_event_id(OWNER *owner, int id) \ { \ tickit_bindings_unbind_event_id(&owner->bindings, owner, id); \ } \ \ static inline void run_events(OWNER *owner, int evindex, \ void *info) \ { \ tickit_bindings_run_event(&owner->bindings, owner, evindex, info); \ } \ \ static inline int run_events_whilefalse(OWNER *owner, int evindex, \ void *info) \ { \ return tickit_bindings_run_event_whilefalse(&owner->bindings, owner, evindex, \ info); \ } libtickit-0.3.4/src/debug.c0000644000000000000000000000761513613430267013661 0ustar 00000000000000#ifdef __GLIBC__ # define _XOPEN_SOURCE // fdopen #endif #include "tickit.h" #include #include #include #include #include // getpid #define streq(a,b) (!strcmp(a,b)) static void (*debug_func)(const char *str, void *data); static void *debug_func_data; static FILE *debug_fh; /* This needs to default true on startup so the first debugging call gets * made, which then lets us inspect the env. vars. to set it correctly */ bool tickit_debug_enabled = true; struct Flag { struct Flag *next; char *name; }; static struct Flag *enabled_flags; static bool init_done = false; void tickit_debug_init(void) { if(init_done) return; const char *flags_str = getenv("TICKIT_DEBUG_FLAGS"); while(flags_str) { const char *endp = strchr(flags_str, ','); if(!endp) endp = flags_str + strlen(flags_str); struct Flag *newflag = malloc(sizeof *newflag); newflag->name = malloc(endp - flags_str + 1); strncpy(newflag->name, flags_str, endp - flags_str); newflag->name[endp - flags_str] = 0; newflag->next = enabled_flags; enabled_flags = newflag; flags_str = endp; if(flags_str[0] == ',') flags_str++; else break; } if(!debug_func) { const char *val; if((val = getenv("TICKIT_DEBUG_FD")) && val[0]) { int fd; if(sscanf(val, "%d", &fd)) tickit_debug_set_fh(fdopen(fd, "a")); } else if((val = getenv("TICKIT_DEBUG_FILE")) && val[0]) { tickit_debug_open(val); } else if(enabled_flags) { char name[17]; sprintf(name, "tickit-%d.log", getpid()); tickit_debug_open(name); } } tickit_debug_enabled = !!enabled_flags && (debug_func || debug_fh); init_done = true; } static bool flag_enabled(const char *name) { for(struct Flag *f = enabled_flags; f; f = f->next) { if(f->name[0] == '*') return true; if(name[0] != f->name[0]) continue; if(!f->name[1]) return true; if(streq(name+1, f->name+1)) return true; } return false; } void tickit_debug_set_func(void (*func)(const char *str, void *data), void *data) { debug_func = func; debug_func_data = data; if(debug_fh) fclose(debug_fh); tickit_debug_enabled = !!enabled_flags && (debug_func || debug_fh); } void tickit_debug_set_fh(FILE *fh) { if(debug_fh) fclose(debug_fh); debug_fh = fh; if(debug_fh) setvbuf(debug_fh, NULL, _IONBF, 0); if(debug_func) debug_func = NULL; tickit_debug_enabled = !!enabled_flags && (debug_func || debug_fh); } bool tickit_debug_open(const char *path) { FILE *fh = fopen(path, "a"); if(!fh) return false; tickit_debug_set_fh(fh); return true; } void tickit_debug_logf(const char *flag, const char *fmt, ...) { va_list args; va_start(args, fmt); tickit_debug_vlogf(flag, fmt, args); va_end(args); } void tickit_debug_vlogf(const char *flag, const char *fmt, va_list args) { if(!init_done) tickit_debug_init(); if(!tickit_debug_enabled) return; if(!flag_enabled(flag)) return; struct timeval now; gettimeofday(&now, NULL); char timestamp[9]; strftime(timestamp, sizeof timestamp, "%H:%M:%S", localtime(&now.tv_sec)); #define LINE_PREFIX "%s.%03d [%-3s]: " if(debug_func) { size_t len; va_list args_copy; va_copy(args_copy, args); len = snprintf(NULL, 0, LINE_PREFIX, timestamp, 0, flag) + vsnprintf(NULL, 0, fmt, args_copy) + 1; va_end(args_copy); char *buf = malloc(len + 1); { char *s = buf; s += sprintf(s, LINE_PREFIX, timestamp, (int)(now.tv_usec / 1000), flag); s += vsprintf(s, fmt, args); s += sprintf(s, "\n"); } (*debug_func)(buf, debug_func_data); free(buf); } else if(debug_fh) { fprintf(debug_fh, LINE_PREFIX, timestamp, (int)(now.tv_usec / 1000), flag); vfprintf(debug_fh, fmt, args); fprintf(debug_fh, "\n"); } #undef LINE_PREFIX } libtickit-0.3.4/src/evloop-default.c0000644000000000000000000000760613613430267015521 0ustar 00000000000000/* We need sigaction() and struct sigaction */ #ifdef __GLIBC__ # define _POSIX_C_SOURCE 199309L #endif #include "tickit.h" #include "tickit-evloop.h" #include #include #include typedef struct { Tickit *t; volatile int still_running; int alloc_fds; int nfds; struct pollfd *pollfds; TickitWatch **pollwatches; unsigned int pending_sigwinch : 1; } EventLoopData; /* TODO: For now this only allows one toplevel instance */ static EventLoopData *sigwinch_observer; static void sigwinch(int signum) { if(sigwinch_observer) sigwinch_observer->pending_sigwinch = true; } static void *evloop_init(Tickit *t, void *initdata) { EventLoopData *evdata = malloc(sizeof(*evdata)); if(!evdata) return NULL; evdata->t = t; evdata->alloc_fds = 4; /* most programs probably won't use more than 1 FD anyway */ evdata->nfds = 0; evdata->pollfds = malloc(sizeof(struct pollfd) * evdata->alloc_fds); evdata->pollwatches = malloc(sizeof(TickitWatch *) * evdata->alloc_fds); sigwinch_observer = evdata; sigaction(SIGWINCH, &(struct sigaction){ .sa_handler = sigwinch }, NULL); return evdata; } static void evloop_destroy(void *data) { EventLoopData *evdata = data; if(evdata->pollfds) free(evdata->pollfds); if(evdata->pollwatches) free(evdata->pollwatches); sigaction(SIGWINCH, &(struct sigaction){ .sa_handler = SIG_DFL }, NULL); sigwinch_observer = NULL; free(evdata); } static void evloop_run(void *data, TickitRunFlags flags) { EventLoopData *evdata = data; evdata->still_running = 1; while(evdata->still_running) { int msec = tickit_evloop_next_timer_msec(evdata->t); if(flags & TICKIT_RUN_NOHANG) msec = 0; int pollret = poll(evdata->pollfds, evdata->nfds, msec); tickit_evloop_invoke_timers(evdata->t); if(pollret > 0) { for(int idx = 0; idx < evdata->nfds; idx++) { if(evdata->pollfds[idx].fd == -1) continue; if(evdata->pollfds[idx].revents & (POLLIN|POLLHUP|POLLERR)) tickit_evloop_invoke_watch(evdata->pollwatches[idx], TICKIT_EV_FIRE); } } else if(pollret < 0 && errno == EINTR) { /* Check the signal flags */ if(evdata->pending_sigwinch) { evdata->pending_sigwinch = false; tickit_evloop_sigwinch(evdata->t); } } if(flags & (TICKIT_RUN_ONCE|TICKIT_RUN_NOHANG)) return; } } static void evloop_stop(void *data) { EventLoopData *evdata = data; evdata->still_running = 0; } static bool evloop_io_read(void *data, int fd, TickitBindFlags flags, TickitWatch *watch) { EventLoopData *evdata = data; int idx; for(idx = 0; idx < evdata->nfds; idx++) if(evdata->pollfds[idx].fd == -1) goto reuse_idx; if(idx == evdata->nfds && evdata->nfds == evdata->alloc_fds) { /* Grow the pollfd array */ struct pollfd *newpollfds = realloc(evdata->pollfds, sizeof(struct pollfd) * evdata->alloc_fds * 2); if(!newpollfds) return false; TickitWatch **newpollwatches = realloc(evdata->pollwatches, sizeof(TickitWatch *) * evdata->alloc_fds * 2); if(!newpollwatches) return false; evdata->alloc_fds *= 2; evdata->pollfds = newpollfds; evdata->pollwatches = newpollwatches; } idx = evdata->nfds; evdata->nfds++; reuse_idx: evdata->pollfds[idx].fd = fd; evdata->pollfds[idx].events = POLLIN; evdata->pollwatches[idx] = watch; tickit_evloop_set_watch_data_int(watch, idx); return true; } static void evloop_cancel_io(void *data, TickitWatch *watch) { EventLoopData *evdata = data; int idx = tickit_evloop_get_watch_data_int(watch); evdata->pollfds[idx].fd = -1; evdata->pollwatches[idx] = NULL; } TickitEventHooks tickit_evloop_default = { .init = evloop_init, .destroy = evloop_destroy, .run = evloop_run, .stop = evloop_stop, .io_read = evloop_io_read, .cancel_io = evloop_cancel_io, }; libtickit-0.3.4/src/linechars.inc0000644000000000000000000000437213613430267015067 0ustar 00000000000000static uint32_t linemask_to_char[] = { // 0x00 0x0000, 0x2575, 0x2575, 0x2579, 0x2576, 0x2514, 0x2559, 0x2516, 0x2576, 0x2558, 0x255a, 0x255a, 0x257a, 0x2515, 0x255a, 0x2517, // 0x10 0x2577, 0x2502, 0x2575, 0x257f, 0x250c, 0x251c, 0x2575, 0x251e, 0x2552, 0x255e, 0x255a, 0x255a, 0x250d, 0x251d, 0x255a, 0x2521, // 0x20 0x2577, 0x2577, 0x2551, 0x2551, 0x2553, 0x2577, 0x255f, 0x2551, 0x2554, 0x2554, 0x2560, 0x2560, 0x2554, 0x2554, 0x2560, 0x2560, // 0x30 0x257b, 0x257d, 0x2551, 0x2503, 0x250e, 0x251f, 0x2551, 0x2520, 0x2554, 0x2554, 0x2560, 0x2560, 0x250f, 0x2522, 0x2560, 0x2523, // 0x40 0x2574, 0x2518, 0x255c, 0x251a, 0x2500, 0x2534, 0x2568, 0x2538, 0x2576, 0x2576, 0x255a, 0x255a, 0x257c, 0x2536, 0x255a, 0x253a, // 0x50 0x2510, 0x2524, 0x2575, 0x2526, 0x252c, 0x253c, 0x2575, 0x2540, 0x2576, 0x2576, 0x255a, 0x255a, 0x252e, 0x253e, 0x255a, 0x2544, // 0x60 0x2556, 0x2577, 0x2562, 0x2551, 0x2565, 0x2577, 0x256b, 0x2551, 0x2554, 0x2554, 0x2560, 0x2560, 0x2554, 0x2554, 0x2560, 0x2560, // 0x70 0x2512, 0x2527, 0x2551, 0x2528, 0x2530, 0x2541, 0x2551, 0x2542, 0x2554, 0x2554, 0x2560, 0x2560, 0x2532, 0x2546, 0x2560, 0x254a, // 0x80 0x2574, 0x255b, 0x255d, 0x255d, 0x2574, 0x2574, 0x255d, 0x255d, 0x2550, 0x2567, 0x2569, 0x2569, 0x2550, 0x2550, 0x2569, 0x2569, // 0x90 0x2555, 0x2561, 0x255d, 0x255d, 0x2574, 0x2574, 0x255d, 0x255d, 0x2564, 0x256a, 0x2569, 0x2569, 0x2550, 0x2550, 0x2569, 0x2569, // 0xa0 0x2557, 0x2557, 0x2563, 0x2563, 0x2557, 0x2557, 0x2563, 0x2563, 0x2566, 0x2566, 0x256c, 0x256c, 0x2566, 0x2566, 0x256c, 0x256c, // 0xb0 0x2557, 0x2557, 0x2563, 0x2563, 0x2557, 0x2557, 0x2563, 0x2563, 0x2566, 0x2566, 0x256c, 0x256c, 0x2566, 0x2566, 0x256c, 0x256c, // 0xc0 0x2578, 0x2519, 0x255d, 0x251b, 0x257e, 0x2535, 0x255d, 0x2539, 0x2550, 0x2550, 0x2569, 0x2569, 0x2501, 0x2537, 0x2569, 0x253b, // 0xd0 0x2511, 0x2525, 0x255d, 0x2529, 0x252d, 0x253d, 0x255d, 0x2543, 0x2550, 0x2550, 0x2569, 0x2569, 0x252f, 0x253f, 0x2569, 0x2547, // 0xe0 0x2557, 0x2557, 0x2563, 0x2563, 0x2557, 0x2557, 0x2563, 0x2563, 0x2566, 0x2566, 0x256c, 0x256c, 0x2566, 0x2566, 0x256c, 0x256c, // 0xf0 0x2513, 0x252a, 0x2563, 0x252b, 0x2531, 0x2545, 0x2563, 0x2549, 0x2566, 0x2566, 0x256c, 0x256c, 0x2533, 0x2548, 0x256c, 0x254b, }; libtickit-0.3.4/src/linechars.inc.PL0000644000000000000000000001556413613430267015406 0ustar 00000000000000#!/usr/bin/perl use strict; use warnings; # Copy from RenderBuffer.pm use constant { LINE_SINGLE => 0x01, LINE_DOUBLE => 0x02, LINE_THICK => 0x03, }; # Bitmasks on Cell linemask use constant { # Connections to the next cell upwards NORTH => 0x03, NORTH_SINGLE => 0x01, NORTH_DOUBLE => 0x02, NORTH_THICK => 0x03, NORTH_SHIFT => 0, # Connections to the next cell to the right EAST => 0x0C, EAST_SINGLE => 0x04, EAST_DOUBLE => 0x08, EAST_THICK => 0x0C, EAST_SHIFT => 2, # Connections to the next cell downwards SOUTH => 0x30, SOUTH_SINGLE => 0x10, SOUTH_DOUBLE => 0x20, SOUTH_THICK => 0x30, SOUTH_SHIFT => 4, # Connections to the next cell to the left WEST => 0xC0, WEST_SINGLE => 0x40, WEST_DOUBLE => 0x80, WEST_THICK => 0xC0, WEST_SHIFT => 6, }; my @linechars; while( ) { chomp; my ( $char, $spec ) = split( m/\s+=>\s+/, $_, 2 ); my $mask = 0; $mask |= __PACKAGE__->$_ for $spec =~ m/([A-Z_]+)/g; $linechars[$mask] = ord $char; } close DATA; # Fill in the gaps foreach my $mask ( 1 .. 255 ) { next if defined $linechars[$mask]; # Try with SINGLE instead of THICK, so mask away 0xAA if( my $char = $linechars[$mask & 0xAA] ) { $linechars[$mask] = $char; next; } # The only ones left now are awkward mixes of single/double # Turn DOUBLE into SINGLE my $singlemask = $mask; foreach my $dir (qw( NORTH EAST SOUTH WEST )) { my $dirmask = __PACKAGE__->$dir; my $dirshift = __PACKAGE__->${\"${dir}_SHIFT"}; my $dirsingle = LINE_SINGLE << $dirshift; my $dirdouble = LINE_DOUBLE << $dirshift; $singlemask = ( $singlemask & ~$dirmask ) | $dirsingle if ( $singlemask & $dirmask ) == $dirdouble; } if( my $char = $linechars[$singlemask] ) { $linechars[$mask] = $char; next; } die sprintf "TODO: Couldn't find a linechar for %02x\n", $mask; } # Now output the mask open STDOUT, ">", ( $0 =~ m/^(.*)\.PL$/ )[0] or die "Cannot write $1 - $!\n"; print <<'EOF'; static uint32_t linemask_to_char[] = { EOF foreach ( 0 .. $#linechars ) { printf " // 0x%02x\n ", $_ if $_%16 == 0; printf "0x%04x, ", $linechars[$_] // 0; printf "\n" if $_%16 == 15; } print <<'EOF'; }; EOF use utf8; __DATA__ ─ => WEST_SINGLE | EAST_SINGLE ━ => WEST_THICK | EAST_THICK │ => NORTH_SINGLE | SOUTH_SINGLE ┃ => NORTH_THICK | SOUTH_THICK ┌ => SOUTH_SINGLE | EAST_SINGLE ┍ => SOUTH_SINGLE | EAST_THICK ┎ => SOUTH_THICK | EAST_SINGLE ┏ => SOUTH_THICK | EAST_THICK ┐ => SOUTH_SINGLE | WEST_SINGLE ┑ => SOUTH_SINGLE | WEST_THICK ┒ => SOUTH_THICK | WEST_SINGLE ┓ => SOUTH_THICK | WEST_THICK └ => NORTH_SINGLE | EAST_SINGLE ┕ => NORTH_SINGLE | EAST_THICK ┖ => NORTH_THICK | EAST_SINGLE ┗ => NORTH_THICK | EAST_THICK ┘ => NORTH_SINGLE | WEST_SINGLE ┙ => NORTH_SINGLE | WEST_THICK ┚ => NORTH_THICK | WEST_SINGLE ┛ => NORTH_THICK | WEST_THICK ├ => NORTH_SINGLE | EAST_SINGLE | SOUTH_SINGLE ┝ => NORTH_SINGLE | SOUTH_SINGLE | EAST_THICK ┞ => NORTH_THICK | EAST_SINGLE | SOUTH_SINGLE ┟ => NORTH_SINGLE | EAST_SINGLE | SOUTH_THICK ┠ => NORTH_THICK | EAST_SINGLE | SOUTH_THICK ┡ => NORTH_THICK | EAST_THICK | SOUTH_SINGLE ┢ => NORTH_SINGLE | EAST_THICK | SOUTH_THICK ┣ => NORTH_THICK | EAST_THICK | SOUTH_THICK ┤ => NORTH_SINGLE | WEST_SINGLE | SOUTH_SINGLE ┥ => NORTH_SINGLE | SOUTH_SINGLE | WEST_THICK ┦ => WEST_SINGLE | NORTH_THICK | SOUTH_SINGLE ┧ => NORTH_SINGLE | WEST_SINGLE | SOUTH_THICK ┨ => WEST_SINGLE | NORTH_THICK | SOUTH_THICK ┩ => WEST_THICK | NORTH_THICK | SOUTH_SINGLE ┪ => WEST_THICK | NORTH_SINGLE | SOUTH_THICK ┫ => WEST_THICK | NORTH_THICK | SOUTH_THICK ┬ => WEST_SINGLE | SOUTH_SINGLE | EAST_SINGLE ┭ => WEST_THICK | SOUTH_SINGLE | EAST_SINGLE ┮ => WEST_SINGLE | SOUTH_SINGLE | EAST_THICK ┯ => WEST_THICK | SOUTH_SINGLE | EAST_THICK ┰ => WEST_SINGLE | SOUTH_THICK | EAST_SINGLE ┱ => WEST_THICK | SOUTH_THICK | EAST_SINGLE ┲ => WEST_SINGLE | SOUTH_THICK | EAST_THICK ┳ => WEST_THICK | SOUTH_THICK | EAST_THICK ┴ => WEST_SINGLE | NORTH_SINGLE | EAST_SINGLE ┵ => WEST_THICK | NORTH_SINGLE | EAST_SINGLE ┶ => WEST_SINGLE | NORTH_SINGLE | EAST_THICK ┷ => WEST_THICK | NORTH_SINGLE | EAST_THICK ┸ => WEST_SINGLE | NORTH_THICK | EAST_SINGLE ┹ => WEST_THICK | NORTH_THICK | EAST_SINGLE ┺ => WEST_SINGLE | NORTH_THICK | EAST_THICK ┻ => WEST_THICK | NORTH_THICK | EAST_THICK ┼ => WEST_SINGLE | NORTH_SINGLE | EAST_SINGLE | SOUTH_SINGLE ┽ => WEST_THICK | NORTH_SINGLE | EAST_SINGLE | SOUTH_SINGLE ┾ => WEST_SINGLE | NORTH_SINGLE | EAST_THICK | SOUTH_SINGLE ┿ => WEST_THICK | NORTH_SINGLE | EAST_THICK | SOUTH_SINGLE ╀ => WEST_SINGLE | NORTH_THICK | EAST_SINGLE | SOUTH_SINGLE ╁ => WEST_SINGLE | NORTH_SINGLE | EAST_SINGLE | SOUTH_THICK ╂ => WEST_SINGLE | NORTH_THICK | EAST_SINGLE | SOUTH_THICK ╃ => WEST_THICK | NORTH_THICK | EAST_SINGLE | SOUTH_SINGLE ╄ => WEST_SINGLE | NORTH_THICK | EAST_THICK | SOUTH_SINGLE ╅ => WEST_THICK | NORTH_SINGLE | EAST_SINGLE | SOUTH_THICK ╆ => WEST_SINGLE | NORTH_SINGLE | EAST_THICK | SOUTH_THICK ╇ => WEST_THICK | NORTH_THICK | EAST_THICK | SOUTH_SINGLE ╈ => WEST_THICK | NORTH_SINGLE | EAST_THICK | SOUTH_THICK ╉ => WEST_THICK | NORTH_THICK | EAST_SINGLE | SOUTH_THICK ╊ => WEST_SINGLE | NORTH_THICK | EAST_THICK | SOUTH_THICK ╋ => WEST_THICK | NORTH_THICK | EAST_THICK | SOUTH_THICK ═ => WEST_DOUBLE | EAST_DOUBLE ║ => NORTH_DOUBLE | SOUTH_DOUBLE ╒ => EAST_DOUBLE | SOUTH_SINGLE ╓ => EAST_SINGLE | SOUTH_DOUBLE ╔ => SOUTH_DOUBLE | EAST_DOUBLE ╕ => WEST_DOUBLE | SOUTH_SINGLE ╖ => WEST_SINGLE | SOUTH_DOUBLE ╗ => WEST_DOUBLE | SOUTH_DOUBLE ╘ => NORTH_SINGLE | EAST_DOUBLE ╙ => NORTH_DOUBLE | EAST_SINGLE ╚ => NORTH_DOUBLE | EAST_DOUBLE ╛ => WEST_DOUBLE | NORTH_SINGLE ╜ => WEST_SINGLE | NORTH_DOUBLE ╝ => WEST_DOUBLE | NORTH_DOUBLE ╞ => NORTH_SINGLE | EAST_DOUBLE | SOUTH_SINGLE ╟ => NORTH_DOUBLE | EAST_SINGLE | SOUTH_DOUBLE ╠ => NORTH_DOUBLE | EAST_DOUBLE | SOUTH_DOUBLE ╡ => WEST_DOUBLE | NORTH_SINGLE | SOUTH_SINGLE ╢ => WEST_SINGLE | NORTH_DOUBLE | SOUTH_DOUBLE ╣ => WEST_DOUBLE | NORTH_DOUBLE | SOUTH_DOUBLE ╤ => WEST_DOUBLE | SOUTH_SINGLE | EAST_DOUBLE ╥ => WEST_SINGLE | SOUTH_DOUBLE | EAST_SINGLE ╦ => WEST_DOUBLE | SOUTH_DOUBLE | EAST_DOUBLE ╧ => WEST_DOUBLE | NORTH_SINGLE | EAST_DOUBLE ╨ => WEST_SINGLE | NORTH_DOUBLE | EAST_SINGLE ╩ => WEST_DOUBLE | NORTH_DOUBLE | EAST_DOUBLE ╪ => WEST_DOUBLE | NORTH_SINGLE | EAST_DOUBLE | SOUTH_SINGLE ╫ => WEST_SINGLE | NORTH_DOUBLE | EAST_SINGLE | SOUTH_DOUBLE ╬ => WEST_DOUBLE | NORTH_DOUBLE | EAST_DOUBLE | SOUTH_DOUBLE ╴ => WEST_SINGLE ╵ => NORTH_SINGLE ╶ => EAST_SINGLE ╷ => SOUTH_SINGLE ╸ => WEST_THICK ╹ => NORTH_THICK ╺ => EAST_THICK ╻ => SOUTH_THICK ╼ => WEST_SINGLE | EAST_THICK ╽ => NORTH_SINGLE | SOUTH_THICK ╾ => WEST_THICK | EAST_SINGLE ╿ => NORTH_THICK | SOUTH_SINGLE libtickit-0.3.4/src/mockterm.c0000644000000000000000000003364313613430267014414 0ustar 00000000000000#ifdef __GLIBC__ # define _XOPEN_SOURCE 700 #endif #include "tickit.h" #include "tickit-mockterm.h" #include "tickit-termdrv.h" #include #include #define BOUND(var,min,max) \ if(var < (min)) var = (min); \ if(var > (max)) var = (max) typedef struct { char *str; TickitPen *pen; } MockTermCell; typedef struct { TickitTermDriver super; int lines; int cols; MockTermCell ***cells; TickitMockTermLogEntry *log; size_t logsize; size_t logi; TickitPen *pen; int line; int col; int cursorvis; int cursorshape; } MockTermDriver; static void mtd_free_cell(MockTermDriver *mtd, int line, int col) { MockTermCell *cell = mtd->cells[line][col]; if(cell->str) free(cell->str); if(cell->pen) tickit_pen_unref(cell->pen); free(cell); } static void mtd_free_line(MockTermDriver *mtd, int line) { for(int col = 0; col < mtd->cols; col++) mtd_free_cell(mtd, line, col); free(mtd->cells[line]); } static void mtd_clear_cells(MockTermDriver *mtd, int line, int startcol, int stopcol) { /* This code is also used to initialise brand new cells in the structure, so * it should be careful to vivify them correctly */ MockTermCell **linecells = mtd->cells[line]; if(!linecells) { linecells = malloc(mtd->cols * sizeof(MockTermCell *)); mtd->cells[line] = linecells; for(int col = 0; col < mtd->cols; col++) linecells[col] = NULL; } for(int col = startcol; col < stopcol; col++) { MockTermCell *cell = linecells[col]; if(!cell) { cell = malloc(sizeof(MockTermCell)); linecells[col] = cell; cell->str = NULL; cell->pen = NULL; } if(cell->str) free(cell->str); if(cell->pen) tickit_pen_unref(cell->pen); cell->str = strdup(" "); cell->pen = tickit_pen_clone(mtd->pen); } } static TickitMockTermLogEntry *mtd_nextlog(MockTermDriver *mtd) { if(mtd->logi == mtd->logsize) { mtd->logsize *= 2; mtd->log = realloc(mtd->log, mtd->logsize * sizeof(TickitMockTermLogEntry)); } TickitMockTermLogEntry *entry = mtd->log + mtd->logi++; entry->str = NULL; entry->pen = NULL; return entry; } static void mtd_free_logentry(TickitMockTermLogEntry *entry) { if(entry->str) free((void *)entry->str); entry->str = NULL; if(entry->pen) tickit_pen_unref(entry->pen); entry->pen = NULL; } static void mtd_destroy(TickitTermDriver *ttd) { MockTermDriver *mtd = (MockTermDriver *)ttd; for(int i = 0; i < mtd->logi; i++) mtd_free_logentry(mtd->log + i); free(mtd->log); for(int line = 0; line < mtd->lines; line++) mtd_free_line(mtd, line); free(mtd->cells); tickit_pen_unref(mtd->pen); free(mtd); } static bool mtd_print(TickitTermDriver *ttd, const char *str, size_t len) { MockTermDriver *mtd = (MockTermDriver *)ttd; TickitMockTermLogEntry *entry = mtd_nextlog(mtd); entry->type = LOG_PRINT; entry->str = strndup(str, len); entry->val1 = len; TickitStringPos pos; tickit_stringpos_zero(&pos); pos.columns = mtd->col; MockTermCell **linecells = mtd->cells[mtd->line]; TickitStringPos limit; tickit_stringpos_limit_columns(&limit, pos.columns); limit.bytes = len; while(pos.bytes < len) { TickitStringPos start = pos; limit.columns++; tickit_utf8_ncountmore(str, len, &pos, &limit); if(pos.columns == start.columns) continue; // Wrap but don't scroll - for now. This shouldn't cause scrolling anyway if(start.columns >= mtd->cols) { start.columns = 0; if(mtd->line < mtd->lines-1) { mtd->line++; linecells = mtd->cells[mtd->line]; } } MockTermCell *cell = linecells[start.columns]; if(cell->str) free(cell->str); if(cell->pen) tickit_pen_unref(cell->pen); cell->str = strndup(str + start.bytes, pos.bytes - start.bytes); cell->pen = tickit_pen_clone(mtd->pen); // Empty out the other cells for doublewidth for(start.columns++; start.columns < pos.columns; start.columns++) { cell = linecells[start.columns]; if(cell->str) free(cell->str); if(cell->pen) tickit_pen_unref(cell->pen); cell->str = NULL; /* empty */ cell->pen = tickit_pen_clone(mtd->pen); } } mtd->col = pos.columns; return true; } static bool mtd_goto_abs(TickitTermDriver *ttd, int line, int col) { MockTermDriver *mtd = (MockTermDriver *)ttd; BOUND(line, 0, mtd->lines-1); BOUND(col, 0, mtd->cols-1); TickitMockTermLogEntry *entry = mtd_nextlog(mtd); entry->type = LOG_GOTO; entry->val1 = line; entry->val2 = col; mtd->line = line; mtd->col = col; return true; } static bool mtd_move_rel(TickitTermDriver *ttd, int downward, int rightward) { MockTermDriver *mtd = (MockTermDriver *)ttd; mtd_goto_abs(ttd, mtd->line + downward, mtd->col + rightward); return true; } static bool mtd_scrollrect(TickitTermDriver *ttd, const TickitRect *rect, int downward, int rightward) { MockTermDriver *mtd = (MockTermDriver *)ttd; if(!downward && !rightward) return true; int top = rect->top; int left = rect->left; int bottom = tickit_rect_bottom(rect); int right = tickit_rect_right(rect); BOUND(top, 0, mtd->lines-1); BOUND(bottom, top, mtd->lines); BOUND(left, 0, mtd->cols-1); BOUND(right, left, mtd->cols); if((abs(downward) >= (bottom - top)) || (abs(rightward) >= (right - left))) return false; if(left == 0 && right == mtd->cols && rightward == 0) { MockTermCell ***cells = mtd->cells; TickitMockTermLogEntry *entry = mtd_nextlog(mtd); entry->type = LOG_SCROLLRECT; entry->val1 = downward; entry->val2 = rightward; entry->rect = *rect; if(downward > 0) { int line; for(line = top; line < top + downward; line++) mtd_free_line(mtd, line); for(line = top; line < bottom - downward; line++) cells[line] = cells[line + downward]; for(/* line */; line < bottom; line++) { cells[line] = NULL; mtd_clear_cells(mtd, line, 0, mtd->cols); } } else { int upward = -downward; int line; for(line = bottom-1; line >= bottom - upward; line--) mtd_free_line(mtd, line); for(line = bottom-1; line >= top + upward; line--) cells[line] = cells[line - upward]; for(/* line */; line >= top; line--) { cells[line] = NULL; mtd_clear_cells(mtd, line, 0, mtd->cols); } } return true; } if(right == mtd->cols && downward == 0) { TickitMockTermLogEntry *entry = mtd_nextlog(mtd); entry->type = LOG_SCROLLRECT; entry->val1 = downward; entry->val2 = rightward; entry->rect = *rect; for(int line = top; line < bottom; line++) { MockTermCell **linecells = mtd->cells[line]; if(rightward > 0) { int col; for(col = left; col < left + rightward; col++) mtd_free_cell(mtd, line, col); for(col = left; col < right - rightward; col++) linecells[col] = linecells[col + rightward]; for(/* col */; col < right; col++) linecells[col] = NULL; mtd_clear_cells(mtd, line, right - rightward, right); } else { int leftward = -rightward; int col; for(col = right-1; col >= right - leftward; col--) mtd_free_cell(mtd, line, col); for(col = right-1; col >= left + leftward; col--) linecells[col] = linecells[col - leftward]; for(/* col */; col >= left; col--) linecells[col] = NULL; mtd_clear_cells(mtd, line, left, left + leftward); } } return true; } return false; } static bool mtd_erasech(TickitTermDriver *ttd, int count, TickitMaybeBool moveend) { MockTermDriver *mtd = (MockTermDriver *)ttd; TickitMockTermLogEntry *entry = mtd_nextlog(mtd); entry->type = LOG_ERASECH; entry->val1 = count; entry->val2 = moveend; int right = mtd->col + count; BOUND(right, 0, mtd->cols); mtd_clear_cells(mtd, mtd->line, mtd->col, right); if(moveend != TICKIT_NO) mtd->col = right; return true; } static bool mtd_clear(TickitTermDriver *ttd) { MockTermDriver *mtd = (MockTermDriver *)ttd; TickitMockTermLogEntry *entry = mtd_nextlog(mtd); entry->type = LOG_CLEAR; for(int line = 0; line < mtd->lines; line++) mtd_clear_cells(mtd, line, 0, mtd->cols); return true; } static bool mtd_chpen(TickitTermDriver *ttd, const TickitPen *delta, const TickitPen *final) { MockTermDriver *mtd = (MockTermDriver *)ttd; TickitMockTermLogEntry *entry = mtd_nextlog(mtd); entry->type = LOG_SETPEN; entry->pen = tickit_pen_clone(final); tickit_pen_clear(mtd->pen); tickit_pen_copy(mtd->pen, final, 1); return true; } static bool mtd_getctl_int(TickitTermDriver *ttd, TickitTermCtl ctl, int *value) { MockTermDriver *mtd = (MockTermDriver *)ttd; switch(ctl) { case TICKIT_TERMCTL_CURSORVIS: *value = mtd->cursorvis; return true; case TICKIT_TERMCTL_CURSORSHAPE: *value = mtd->cursorshape; return true; case TICKIT_TERMCTL_COLORS: *value = 256; return true; default: return false; } } static bool mtd_setctl_int(TickitTermDriver *ttd, TickitTermCtl ctl, int value) { MockTermDriver *mtd = (MockTermDriver *)ttd; switch(ctl) { case TICKIT_TERMCTL_CURSORVIS: mtd->cursorvis = !!value; break; case TICKIT_TERMCTL_CURSORSHAPE: mtd->cursorshape = value; break; case TICKIT_TERMCTL_ALTSCREEN: case TICKIT_TERMCTL_MOUSE: break; default: return false; } return true; } static TickitTermDriverVTable mtd_vtable = { .destroy = mtd_destroy, .print = mtd_print, .goto_abs = mtd_goto_abs, .move_rel = mtd_move_rel, .scrollrect = mtd_scrollrect, .erasech = mtd_erasech, .clear = mtd_clear, .chpen = mtd_chpen, .getctl_int = mtd_getctl_int, .setctl_int = mtd_setctl_int, }; TickitMockTerm *tickit_mockterm_new(int lines, int cols) { MockTermDriver *mtd = malloc(sizeof(MockTermDriver)); mtd->super.vtable = &mtd_vtable; mtd->logsize = 16; // should be sufficient; or it will grow mtd->log = malloc(mtd->logsize * sizeof(TickitMockTermLogEntry)); mtd->logi = 0; mtd->pen = tickit_pen_new(); mtd->lines = lines; mtd->cols = cols; mtd->line = -1; mtd->col = -1; mtd->cursorvis = 0; mtd->cursorshape = 0; mtd->cells = malloc(lines * sizeof(MockTermCell **)); for(int line = 0; line < lines; line++) { mtd->cells[line] = NULL; mtd_clear_cells(mtd, line, 0, cols); } TickitMockTerm *mt = (TickitMockTerm *)tickit_term_new_for_driver(&mtd->super); if(!mt) { mtd_destroy((TickitTermDriver *)mtd); return NULL; } tickit_term_set_size((TickitTerm *)mt, lines, cols); return mt; } void tickit_mockterm_destroy(TickitMockTerm *mt) { tickit_term_destroy((TickitTerm *)mt); } size_t tickit_mockterm_get_display_text(TickitMockTerm *mt, char *buffer, size_t len, int line, int col, int width) { MockTermDriver *mtd = (MockTermDriver *)tickit_term_get_driver((TickitTerm *)mt); MockTermCell **linecells = mtd->cells[line]; size_t ret = 0; for(/* col */; width; col++, width--) { MockTermCell *cell = linecells[col]; size_t celllen = cell->str ? strlen(cell->str) : 0; if(buffer && celllen && len >= celllen) { strcpy(buffer, cell->str); buffer += celllen; len -= celllen; if(len <= 0) buffer = NULL; } ret += celllen; } return ret; } TickitPen *tickit_mockterm_get_display_pen(TickitMockTerm *mt, int line, int col) { MockTermDriver *mtd = (MockTermDriver *)tickit_term_get_driver((TickitTerm *)mt); return mtd->cells[line][col]->pen; } void tickit_mockterm_resize(TickitMockTerm *mt, int newlines, int newcols) { MockTermDriver *mtd = (MockTermDriver *)tickit_term_get_driver((TickitTerm *)mt); MockTermCell ***newcells = malloc(newlines * sizeof(MockTermCell **)); int oldlines = mtd->lines; int oldcols = mtd->cols; int line; for(line = newlines; line < oldlines; line++) mtd_free_line(mtd, line); for(line = 0; line < newlines && line < oldlines; line++) { MockTermCell **newlinecells; if(newcols == oldcols) newlinecells = mtd->cells[line]; else { newlinecells = malloc(newcols * sizeof(MockTermCell *)); int col; for(col = newcols; col < oldcols; col++) mtd_free_cell(mtd, line, col); for(col = 0; col < newcols && col < oldcols; col++) newlinecells[col] = mtd->cells[line][col]; for(/* col */; col < newcols; col++) newlinecells[col] = NULL; free(mtd->cells[line]); } newcells[line] = newlinecells; } for(/* line */; line < newlines; line++) newcells[line] = NULL; free(mtd->cells); mtd->cells = newcells; mtd->lines = newlines; mtd->cols = newcols; if(newcols > oldcols) for(line = 0; line < newlines && line < oldlines; line++) mtd_clear_cells(mtd, line, oldcols, newcols); for(line = oldlines; line < newlines; line++) mtd_clear_cells(mtd, line, 0, newcols); tickit_term_set_size((TickitTerm *)mt, newlines, newcols); BOUND(mtd->line, 0, mtd->lines-1); BOUND(mtd->col, 0, mtd->cols-1); } int tickit_mockterm_loglen(TickitMockTerm *mt) { MockTermDriver *mtd = (MockTermDriver *)tickit_term_get_driver((TickitTerm *)mt); return mtd->logi; } TickitMockTermLogEntry *tickit_mockterm_peeklog(TickitMockTerm *mt, int i) { MockTermDriver *mtd = (MockTermDriver *)tickit_term_get_driver((TickitTerm *)mt); if(i >= 0 && i < mtd->logi) return mtd->log + i; return NULL; } void tickit_mockterm_clearlog(TickitMockTerm *mt) { MockTermDriver *mtd = (MockTermDriver *)tickit_term_get_driver((TickitTerm *)mt); for(int i = 0; i < mtd->logi; i++) mtd_free_logentry(mtd->log + i); mtd->logi = 0; } void tickit_mockterm_get_position(TickitMockTerm *mt, int *line, int *col) { MockTermDriver *mtd = (MockTermDriver *)tickit_term_get_driver((TickitTerm *)mt); if(line) *line = mtd->line; if(col) *col = mtd->col; } libtickit-0.3.4/src/pen.c0000644000000000000000000003377313613430267013361 0ustar 00000000000000#include "tickit.h" #include "bindings.h" #include #include /* sscanf */ #include #include #define streq(a,b) (!strcmp(a,b)) #define COLOUR_DEFAULT -1 struct TickitPen { signed int fgindex : 9, /* 0 - 255 or COLOUR_DEFAULT */ bgindex : 9; /* 0 - 255 or COLOUR_DEFAULT */ TickitPenRGB8 fg_rgb8, bg_rgb8; unsigned int bold : 1, italic : 1, reverse : 1, strike : 1, blink : 1; signed int under : 3; /* 0 to 3 or -1 */ signed int altfont : 5; /* 1 - 10 or -1 */ struct { unsigned int fgindex : 1, bgindex : 1, fg_rgb8 : 1, bg_rgb8 : 1, bold : 1, under : 1, italic : 1, reverse : 1, strike : 1, altfont : 1, blink : 1; } valid; int refcount; struct TickitBindings bindings; int freezecount; bool changed; }; DEFINE_BINDINGS_FUNCS(pen,TickitPen,TickitPenEventFn) TickitPen *tickit_pen_new(void) { TickitPen *pen = malloc(sizeof(TickitPen)); if(!pen) return NULL; pen->refcount = 1; pen->bindings = (struct TickitBindings){ NULL }; pen->freezecount = 0; pen->changed = false; tickit_pen_clear(pen); return pen; } TickitPen *tickit_pen_new_attrs(TickitPenAttr attr, ...) { TickitPen *pen = tickit_pen_new(); if(!pen) return NULL; va_list args; va_start(args, attr); int first = 1; int val; while(1) { int a = first ? attr : va_arg(args, TickitPenAttr); first = 0; if(a < 1) break; // TODO: accept colour descs or rgb8 switch(tickit_pen_attrtype(a)) { case TICKIT_PENTYPE_BOOL: val = va_arg(args, int); tickit_pen_set_bool_attr(pen, a, val); break; case TICKIT_PENTYPE_INT: val = va_arg(args, int); tickit_pen_set_int_attr(pen, a, val); break; case TICKIT_PENTYPE_COLOUR: val = va_arg(args, int); tickit_pen_set_colour_attr(pen, a, val); break; } } va_end(args); return pen; } TickitPen *tickit_pen_clone(const TickitPen *orig) { TickitPen *pen = tickit_pen_new(); tickit_pen_copy(pen, orig, true); return pen; } static void destroy(TickitPen *pen) { tickit_bindings_unbind_and_destroy(&pen->bindings, pen); free(pen); } static void changed(TickitPen *pen) { if(!pen->freezecount) run_events(pen, TICKIT_PEN_ON_CHANGE, NULL); else pen->changed = true; } static void freeze(TickitPen *pen) { pen->freezecount++; } static void thaw(TickitPen *pen) { pen->freezecount--; if(!pen->freezecount && pen->changed) { run_events(pen, TICKIT_PEN_ON_CHANGE, NULL); pen->changed = false; } } TickitPen *tickit_pen_ref(TickitPen *pen) { pen->refcount++; return pen; } void tickit_pen_unref(TickitPen *pen) { if(pen->refcount < 1) { fprintf(stderr, "tickit_pen_unref: invalid refcount %d\n", pen->refcount); abort(); } pen->refcount--; if(!pen->refcount) destroy(pen); } bool tickit_pen_has_attr(const TickitPen *pen, TickitPenAttr attr) { switch(attr) { case TICKIT_PEN_FG: return pen->valid.fgindex; case TICKIT_PEN_BG: return pen->valid.bgindex; case TICKIT_PEN_BOLD: return pen->valid.bold; case TICKIT_PEN_UNDER: return pen->valid.under; case TICKIT_PEN_ITALIC: return pen->valid.italic; case TICKIT_PEN_REVERSE: return pen->valid.reverse; case TICKIT_PEN_STRIKE: return pen->valid.strike; case TICKIT_PEN_ALTFONT: return pen->valid.altfont; case TICKIT_PEN_BLINK: return pen->valid.blink; case TICKIT_N_PEN_ATTRS: return false; } return false; } bool tickit_pen_nondefault_attr(const TickitPen *pen, TickitPenAttr attr) { if(!tickit_pen_has_attr(pen, attr)) return false; switch(tickit_pen_attrtype(attr)) { case TICKIT_PENTYPE_BOOL: if(tickit_pen_get_bool_attr(pen, attr)) return true; break; case TICKIT_PENTYPE_INT: if(tickit_pen_get_int_attr(pen, attr) > 0) return true; break; case TICKIT_PENTYPE_COLOUR: if(tickit_pen_get_colour_attr(pen, attr) != COLOUR_DEFAULT) return true; break; } return false; } bool tickit_pen_is_nonempty(const TickitPen *pen) { for(TickitPenAttr attr = 1; attr < TICKIT_N_PEN_ATTRS; attr++) { if(tickit_pen_has_attr(pen, attr)) return true; } return false; } bool tickit_pen_is_nondefault(const TickitPen *pen) { for(TickitPenAttr attr = 1; attr < TICKIT_N_PEN_ATTRS; attr++) { if(tickit_pen_nondefault_attr(pen, attr)) return true; } return false; } bool tickit_pen_get_bool_attr(const TickitPen *pen, TickitPenAttr attr) { if(!tickit_pen_has_attr(pen, attr)) return false; switch(attr) { case TICKIT_PEN_BOLD: return pen->bold; case TICKIT_PEN_ITALIC: return pen->italic; case TICKIT_PEN_REVERSE: return pen->reverse; case TICKIT_PEN_STRIKE: return pen->strike; case TICKIT_PEN_BLINK: return pen->blink; /* back-compat */ case TICKIT_PEN_UNDER: return pen->under > 0; default: return false; } } void tickit_pen_set_bool_attr(TickitPen *pen, TickitPenAttr attr, bool val) { switch(attr) { case TICKIT_PEN_BOLD: pen->bold = !!val; pen->valid.bold = 1; break; case TICKIT_PEN_ITALIC: pen->italic = !!val; pen->valid.italic = 1; break; case TICKIT_PEN_REVERSE: pen->reverse = !!val; pen->valid.reverse = 1; break; case TICKIT_PEN_STRIKE: pen->strike = !!val; pen->valid.strike = 1; break; case TICKIT_PEN_BLINK: pen->blink = !!val; pen->valid.blink = 1; break; /* back-compat */ case TICKIT_PEN_UNDER: pen->under = val ? TICKIT_PEN_UNDER_SINGLE : TICKIT_PEN_UNDER_NONE; pen->valid.under = 1; break; default: return; } changed(pen); } int tickit_pen_get_int_attr(const TickitPen *pen, TickitPenAttr attr) { if(!tickit_pen_has_attr(pen, attr)) return 0; switch(attr) { case TICKIT_PEN_UNDER: return pen->under; case TICKIT_PEN_ALTFONT: return pen->altfont; default: return 0; } } void tickit_pen_set_int_attr(TickitPen *pen, TickitPenAttr attr, int val) { switch(attr) { case TICKIT_PEN_UNDER: pen->under = val; pen->valid.under = 1; break; case TICKIT_PEN_ALTFONT: pen->altfont = val; pen->valid.altfont = 1; break; default: return; } changed(pen); } int tickit_pen_get_colour_attr(const TickitPen *pen, TickitPenAttr attr) { if(!tickit_pen_has_attr(pen, attr)) return COLOUR_DEFAULT; switch(attr) { case TICKIT_PEN_FG: return pen->fgindex; case TICKIT_PEN_BG: return pen->bgindex; default: return 0; } } void tickit_pen_set_colour_attr(TickitPen *pen, TickitPenAttr attr, int val) { switch(attr) { case TICKIT_PEN_FG: pen->fgindex = val; pen->valid.fgindex = 1; pen->valid.fg_rgb8 = 0; break; case TICKIT_PEN_BG: pen->bgindex = val; pen->valid.bgindex = 1; pen->valid.bg_rgb8 = 0; break; default: return; } run_events(pen, TICKIT_PEN_ON_CHANGE, NULL); } bool tickit_pen_has_colour_attr_rgb8(const TickitPen *pen, TickitPenAttr attr) { switch(attr) { case TICKIT_PEN_FG: return pen->valid.fgindex && pen->valid.fg_rgb8; case TICKIT_PEN_BG: return pen->valid.bgindex && pen->valid.bg_rgb8; default: return 0; } } TickitPenRGB8 tickit_pen_get_colour_attr_rgb8(const TickitPen *pen, TickitPenAttr attr) { if(tickit_pen_has_colour_attr_rgb8(pen, attr)) switch(attr) { case TICKIT_PEN_FG: return pen->fg_rgb8; case TICKIT_PEN_BG: return pen->bg_rgb8; default: break; } return (TickitPenRGB8){0, 0, 0}; } void tickit_pen_set_colour_attr_rgb8(TickitPen *pen, TickitPenAttr attr, TickitPenRGB8 val) { /* can only set an RGB8 version if the regular index version is already set */ if(!tickit_pen_has_attr(pen, attr)) return; switch(attr) { case TICKIT_PEN_FG: pen->fg_rgb8 = val; pen->valid.fg_rgb8 = 1; break; case TICKIT_PEN_BG: pen->bg_rgb8 = val; pen->valid.bg_rgb8 = 1; break; default: return; } changed(pen); } static struct { const char *name; int colour; } colournames[] = { { "black", 0 }, { "red", 1 }, { "green", 2 }, { "yellow", 3 }, { "blue", 4 }, { "magenta", 5 }, { "cyan", 6 }, { "white", 7 }, // 256-colour palette { "grey", 8 }, { "brown", 94 }, { "orange", 208 }, { "pink", 212 }, { "purple", 128 }, }; bool tickit_pen_set_colour_attr_desc(TickitPen *pen, TickitPenAttr attr, const char *desc) { int hi = 0; int val; if(strncmp(desc, "hi-", 3) == 0) { desc += 3; hi = 8; } if(sscanf(desc, "%d", &val) == 1) { if(hi && val > 7) return false; tickit_pen_set_colour_attr(pen, attr, val + hi); return true; } for(int i = 0; i < sizeof(colournames)/sizeof(colournames[0]); i++) { if(strcmp(desc, colournames[i].name) != 0) continue; val = colournames[i].colour; if(val < 8 && hi) val += hi; tickit_pen_set_colour_attr(pen, attr, val); return true; } return false; } void tickit_pen_clear_attr(TickitPen *pen, TickitPenAttr attr) { switch(attr) { case TICKIT_PEN_FG: pen->valid.fgindex = 0; break; case TICKIT_PEN_BG: pen->valid.bgindex = 0; break; case TICKIT_PEN_BOLD: pen->valid.bold = 0; break; case TICKIT_PEN_UNDER: pen->valid.under = 0; break; case TICKIT_PEN_ITALIC: pen->valid.italic = 0; break; case TICKIT_PEN_REVERSE: pen->valid.reverse = 0; break; case TICKIT_PEN_STRIKE: pen->valid.strike = 0; break; case TICKIT_PEN_ALTFONT: pen->valid.altfont = 0; break; case TICKIT_PEN_BLINK: pen->valid.blink = 0; break; case TICKIT_N_PEN_ATTRS: return; } changed(pen); } void tickit_pen_clear(TickitPen *pen) { for(TickitPenAttr attr = 1; attr < TICKIT_N_PEN_ATTRS; attr++) tickit_pen_clear_attr(pen, attr); } bool tickit_pen_equiv_attr(const TickitPen *a, const TickitPen *b, TickitPenAttr attr) { switch(tickit_pen_attrtype(attr)) { case TICKIT_PENTYPE_BOOL: return tickit_pen_get_bool_attr(a, attr) == tickit_pen_get_bool_attr(b, attr); case TICKIT_PENTYPE_INT: return tickit_pen_get_int_attr(a, attr) == tickit_pen_get_int_attr(b, attr); case TICKIT_PENTYPE_COLOUR: if(tickit_pen_get_colour_attr(a, attr) != tickit_pen_get_colour_attr(b, attr)) return false; /* indexes are equal; now compare RGB8s */ if(!tickit_pen_has_colour_attr_rgb8(a, attr) && !tickit_pen_has_colour_attr_rgb8(b, attr)) return true; if(!tickit_pen_has_colour_attr_rgb8(a, attr) || !tickit_pen_has_colour_attr_rgb8(b, attr)) return false; TickitPenRGB8 acol = tickit_pen_get_colour_attr_rgb8(a, attr), bcol = tickit_pen_get_colour_attr_rgb8(b, attr); return (acol.r == bcol.r) && (acol.g == bcol.g) && (acol.b == bcol.b); } return false; } bool tickit_pen_equiv(const TickitPen *a, const TickitPen *b) { if(a == b) return true; for(TickitPenAttr attr = 1; attr < TICKIT_N_PEN_ATTRS; attr++) if(!tickit_pen_equiv_attr(a, b, attr)) return false; return true; } void tickit_pen_copy_attr(TickitPen *dst, const TickitPen *src, TickitPenAttr attr) { switch(tickit_pen_attrtype(attr)) { case TICKIT_PENTYPE_BOOL: tickit_pen_set_bool_attr(dst, attr, tickit_pen_get_bool_attr(src, attr)); return; case TICKIT_PENTYPE_INT: tickit_pen_set_int_attr(dst, attr, tickit_pen_get_int_attr(src, attr)); return; case TICKIT_PENTYPE_COLOUR: freeze(dst); tickit_pen_set_colour_attr(dst, attr, tickit_pen_get_colour_attr(src, attr)); if(tickit_pen_has_colour_attr_rgb8(src, attr)) tickit_pen_set_colour_attr_rgb8(dst, attr, tickit_pen_get_colour_attr_rgb8(src, attr)); thaw(dst); return; } return; } void tickit_pen_copy(TickitPen *dst, const TickitPen *src, bool overwrite) { freeze(dst); for(TickitPenAttr attr = 1; attr < TICKIT_N_PEN_ATTRS; attr++) { if(!tickit_pen_has_attr(src, attr)) continue; if(tickit_pen_has_attr(dst, attr) && (!overwrite || tickit_pen_equiv_attr(src, dst, attr))) continue; tickit_pen_copy_attr(dst, src, attr); } thaw(dst); } TickitPenAttrType tickit_pen_attrtype(TickitPenAttr attr) { switch(attr) { case TICKIT_PEN_FG: case TICKIT_PEN_BG: return TICKIT_PENTYPE_COLOUR; case TICKIT_PEN_ALTFONT: case TICKIT_PEN_UNDER: return TICKIT_PENTYPE_INT; case TICKIT_PEN_BOLD: case TICKIT_PEN_ITALIC: case TICKIT_PEN_REVERSE: case TICKIT_PEN_STRIKE: case TICKIT_PEN_BLINK: return TICKIT_PENTYPE_BOOL; case TICKIT_N_PEN_ATTRS: return -1; } return -1; } const char *tickit_pen_attrname(TickitPenAttr attr) { switch(attr) { case TICKIT_PEN_FG: return "fg"; case TICKIT_PEN_BG: return "bg"; case TICKIT_PEN_BOLD: return "b"; case TICKIT_PEN_UNDER: return "u"; case TICKIT_PEN_ITALIC: return "i"; case TICKIT_PEN_REVERSE: return "rv"; case TICKIT_PEN_STRIKE: return "strike"; case TICKIT_PEN_ALTFONT: return "af"; case TICKIT_PEN_BLINK: return "blink"; case TICKIT_N_PEN_ATTRS: ; } return NULL; } TickitPenAttr tickit_pen_lookup_attr(const char *name) { switch(name[0]) { case 'a': return streq(name+1,"f") ? TICKIT_PEN_ALTFONT : -1; case 'b': return name[1] == 0 ? TICKIT_PEN_BOLD : streq(name+1,"g") ? TICKIT_PEN_BG : streq(name+1,"link") ? TICKIT_PEN_BLINK : -1; case 'f': return streq(name+1,"g") ? TICKIT_PEN_FG : -1; case 'i': return name[1] == 0 ? TICKIT_PEN_ITALIC : -1; case 'r': return streq(name+1,"v") ? TICKIT_PEN_REVERSE : -1; case 's': return streq(name+1,"trike") ? TICKIT_PEN_STRIKE : -1; case 'u': return name[1] == 0 ? TICKIT_PEN_UNDER : -1; } return -1; } libtickit-0.3.4/src/rect.c0000644000000000000000000001117513613430267013524 0ustar 00000000000000#include "tickit.h" static inline int minint(int a, int b) { return a < b ? a : b; } static inline int maxint(int a, int b) { return a > b ? a : b; } void tickit_rect_init_sized(TickitRect *rect, int top, int left, int lines, int cols) { rect->top = top; rect->left = left; rect->lines = lines; rect->cols = cols; } void tickit_rect_init_bounded(TickitRect *rect, int top, int left, int bottom, int right) { rect->top = top; rect->left = left; rect->lines = bottom - top; rect->cols = right - left; } void tickit_rect_translate(TickitRect *rect, int downward, int rightward) { rect->top += downward; rect->left += rightward; } bool tickit_rect_intersect(TickitRect *dst, const TickitRect *a, const TickitRect *b) { int top = maxint(a->top, b->top); int bottom = minint(tickit_rect_bottom(a), tickit_rect_bottom(b)); if(top >= bottom) return false; int left = maxint(a->left, b->left); int right = minint(tickit_rect_right(a), tickit_rect_right(b)); if(left >= right) return false; tickit_rect_init_bounded(dst, top, left, bottom, right); return true; } bool tickit_rect_intersects(const TickitRect *a, const TickitRect *b) { return a->top < tickit_rect_bottom(b) && b->top < tickit_rect_bottom(a) && a->left < tickit_rect_right(b) && b->left < tickit_rect_right(a); } bool tickit_rect_contains(const TickitRect *large, const TickitRect *small) { return (small->top >= large->top ) && (tickit_rect_bottom(small) <= tickit_rect_bottom(large)) && (small->left >= large->left ) && (tickit_rect_right(small) <= tickit_rect_right(large) ); } int tickit_rect_add(TickitRect ret[3], const TickitRect *a, const TickitRect *b) { int a_bottom = tickit_rect_bottom(a); int a_right = tickit_rect_right(a); int b_bottom = tickit_rect_bottom(b); int b_right = tickit_rect_right(b); if(a->left > b_right || b->left > a_right || a->top > b_bottom || b->top > a_bottom) { ret[0] = *a; ret[1] = *b; return 2; } int rows[4]; rows[0] = a->top; rows[1] = b->top; rows[2] = a_bottom; rows[3] = b_bottom; /* Since we know top < bottom for each rect individually we can do this * better than a plain sort */ if(rows[0] > rows[1]) { int tmp = rows[0]; rows[0] = rows[1]; rows[1] = tmp; } // tops now in order if(rows[2] > rows[3]) { int tmp = rows[2]; rows[2] = rows[3]; rows[3] = tmp; } // bottoms now in order if(rows[1] > rows[2]) { int tmp = rows[1]; rows[1] = rows[2]; rows[2] = tmp; } // all now in order int rects = 0; for(int i = 0; i < 3; i++ ) { int this_top = rows[i]; int this_bottom = rows[i+1]; // Skip non-unique if(this_top == this_bottom) continue; int has_a = this_top >= a->top && this_bottom <= a_bottom; int has_b = this_top >= b->top && this_bottom <= b_bottom; int this_left = (has_a && has_b) ? minint(a->left, b->left) : (has_a) ? a->left : b->left; int this_right = (has_a && has_b) ? maxint(a_right, b_right) : (has_a) ? a_right : b_right; if(rects && ret[rects-1].left == this_left && ret[rects-1].cols == this_right - this_left) { ret[rects-1].lines = this_bottom - ret[rects-1].top; } else { tickit_rect_init_bounded(ret + rects, this_top, this_left, this_bottom, this_right); rects++; } } return rects; } int tickit_rect_subtract(TickitRect ret[5], const TickitRect *orig, const TickitRect *hole) { if(tickit_rect_contains(hole, orig)) return 0; if(!tickit_rect_intersects(hole, orig)) { ret[0] = *orig; return 1; } int rects = 0; int orig_right = tickit_rect_right(orig); int hole_right = tickit_rect_right(hole); if(orig->top < hole->top) { tickit_rect_init_bounded(ret + rects, orig->top, orig->left, hole->top, orig_right); rects++; } int orig_bottom = tickit_rect_bottom(orig); int hole_bottom = tickit_rect_bottom(hole); int mid_top = maxint(orig->top, hole->top); int mid_bottom = minint(orig_bottom, hole_bottom); if(orig->left < hole->left) { tickit_rect_init_bounded(ret + rects, mid_top, orig->left, mid_bottom, hole->left); rects++; } if(orig_right > hole_right) { tickit_rect_init_bounded(ret + rects, mid_top, hole_right, mid_bottom, orig_right); rects++; } if(orig_bottom > hole_bottom) { tickit_rect_init_bounded(ret + rects, hole_bottom, orig->left, orig_bottom, orig_right); rects++; } return rects; } libtickit-0.3.4/src/rectset.c0000644000000000000000000001415613613430267014242 0ustar 00000000000000#include "tickit.h" #include // memcpy, memmove struct TickitRectSet { TickitRect *rects; size_t count; /* How many we consider valid */ size_t size; /* How many can fit in the allocated array */ }; TickitRectSet *tickit_rectset_new(void) { TickitRectSet *ret = malloc(sizeof(TickitRectSet)); if(!ret) return NULL; ret->size = 4; ret->rects = malloc(ret->size * sizeof(ret->rects[0])); if(!ret->rects) goto abort_free; ret->count = 0; return ret; abort_free: free(ret); return NULL; } void tickit_rectset_destroy(TickitRectSet *trs) { free(trs->rects); free(trs); } void tickit_rectset_clear(TickitRectSet *trs) { trs->count = 0; } size_t tickit_rectset_rects(const TickitRectSet *trs) { return trs->count; } size_t tickit_rectset_get_rect(const TickitRectSet *trs, size_t i, TickitRect *rect) { if(i >= trs->count) return 0; memcpy(rect, trs->rects + i, sizeof(trs->rects[0])); return 1; } size_t tickit_rectset_get_rects(const TickitRectSet *trs, TickitRect rects[], size_t n) { if(n > trs->count) n = trs->count; memcpy(rects, trs->rects, n * sizeof(trs->rects[0])); return n; } static int cmprect(const TickitRect *a, const TickitRect *b) { if(a->top != b->top) return a->top - b->top; return a->left - b->left; } static int insert_rect(TickitRectSet *trs, const TickitRect *r) { if(trs->count + 1 > trs->size) { TickitRect *newrects = realloc(trs->rects, trs->size * 2 * sizeof(trs->rects[0])); if(!newrects) return 0; trs->rects = newrects; trs->size *= 2; } /* TODO: binary search */ int idx; for(idx = 0; idx < trs->count; idx++) if(cmprect(trs->rects + idx, r) > 0) break; memmove(trs->rects + idx + 1, trs->rects + idx, (trs->count - idx) * sizeof(trs->rects[0])); trs->rects[idx] = *r; trs->count++; return 1; } static void delete_rect(TickitRectSet *trs, int idx) { memmove(trs->rects + idx, trs->rects + idx + 1, (trs->count - idx - 1) * sizeof(trs->rects[0])); trs->count--; } void tickit_rectset_add(TickitRectSet *trs, const TickitRect *rect) { int top = rect->top; int bottom = tickit_rect_bottom(rect); int left = rect->left; int right = tickit_rect_right(rect); restart: for(int i = 0; i < trs->count; i++) { TickitRect *r = trs->rects + i; int r_bottom = tickit_rect_bottom(r); int r_right = tickit_rect_right(r); // Because the rects are ordered, there is no point continuing if we're // already past it if(bottom < r->top) break; if(top > r_bottom || left > r_right || right < r->left) continue; if(tickit_rect_contains(r, rect)) // Already entirely covered, just return return; int top_eq = top == r->top; int bottom_eq = bottom == r_bottom; int left_eq = left == r->left; int right_eq = right == r_right; if((top_eq && bottom_eq) || (left_eq && right_eq)) { // Stretch an existing rectangle horizontally or vertically // Tempting to think we can just do this in-place but needs to account // for being able to merge multiple at once if(r->top < top) top = r->top; if(r_bottom > bottom) bottom = r_bottom; if(r->left < left) left = r->left; if(r_right > right) right = r_right; delete_rect(trs, i); goto restart; } if(top == r_bottom || bottom == r->top) // No actual interaction, just add it. This case handles the recursion // implied at the end of this loop continue; // Non-simple interaction. Split r and rect into the 2 or 3 separate rects // it now must be composed of, delete r, then recurse on those to-be-added // rects instead. TickitRect to_add[3]; int n = tickit_rect_add(to_add, r, rect); // TODO: top/left/bottom/right ? delete_rect(trs, i); for(i = 0; i < n; i++) tickit_rectset_add(trs, to_add + i); return; } // If we got this far then we need to add it TickitRect new; tickit_rect_init_bounded(&new, top, left, bottom, right); // TODO: error handling insert_rect(trs, &new); } void tickit_rectset_subtract(TickitRectSet *trs, const TickitRect *rect) { for(int i = 0; i < trs->count; i++) { TickitRect *r = trs->rects + i; if(!tickit_rect_intersects(r, rect)) continue; TickitRect remains[4]; int n = tickit_rect_subtract(remains, r, rect); delete_rect(trs, i); i--; // It doesn't matter if insert starts putting these before i; the worst // that will happen is that a few rects that we inspected once just move // underneath us and we have to inspect them a second time int j; for(j = 0; j < n; j++) tickit_rectset_add(trs, remains + j); } } void tickit_rectset_translate(TickitRectSet *trs, int downward, int rightward) { for(int i = 0; i < trs->count; i++) { trs->rects[i].top += downward; trs->rects[i].left += rightward; } } bool tickit_rectset_intersects(const TickitRectSet *trs, const TickitRect *rect) { for(int i = 0; i < trs->count; i++) if(tickit_rect_intersects(trs->rects + i, rect)) return true; return false; } bool tickit_rectset_contains(const TickitRectSet *trs, const TickitRect *rectptr) { // We might want to modify it TickitRect rect = *rectptr; for(int i = 0; i < trs->count; i++) { TickitRect *r = trs->rects + i; if(!tickit_rect_intersects(r, &rect)) continue; // Because rects are in order, if there's any part of rect above or to // the left of here we know we didn't match it if(rect.top < r->top || rect.left < r->left) return false; int r_bottom = tickit_rect_bottom(r); int rect_bottom = tickit_rect_bottom(&rect); if(rect.top < r_bottom && r_bottom < rect_bottom) { int rect_right = tickit_rect_right(&rect); TickitRect lower; tickit_rect_init_bounded(&lower, r_bottom, rect.left, rect_bottom, rect_right); if(!tickit_rectset_contains(trs, &lower)) return false; // tickit_rect_init_bounded(&rect, rect.top, rect.left, r_bottom, r_right) rect.lines = r_bottom - rect.top; } return tickit_rect_contains(r, &rect); } return false; } libtickit-0.3.4/src/renderbuffer.c0000644000000000000000000010116713613430267015241 0ustar 00000000000000#ifdef __GLIBC__ # define _XOPEN_SOURCE 600 /* strdup */ #endif #include "tickit.h" #include // vsnprintf #include #include #include "linechars.inc" #define RECT_PRINTF_FMT "[(%d,%d)..(%d,%d)]" #define RECT_PRINTF_ARGS(r) (r).left, (r).top, tickit_rect_right(&(r)), tickit_rect_bottom(&(r)) /* must match .pm file */ enum TickitRenderBufferCellState { SKIP = 0, TEXT = 1, ERASE = 2, CONT = 3, LINE = 4, CHAR = 5, }; enum { NORTH_SHIFT = 0, EAST_SHIFT = 2, SOUTH_SHIFT = 4, WEST_SHIFT = 6, }; // Internal cell structure definition typedef struct { enum TickitRenderBufferCellState state; union { int startcol; // for state == CONT int cols; // otherwise }; int maskdepth; // -1 if not masked TickitPen *pen; // state -> {TEXT, ERASE, LINE, CHAR} union { struct { TickitString *s; int offs; } text; // state == TEXT struct { int mask; } line; // state == LINE struct { int codepoint; } chr; // state == CHAR } v; } RBCell; typedef struct RBStack RBStack; struct RBStack { RBStack *prev; int vc_line, vc_col; int xlate_line, xlate_col; TickitRect clip; TickitPen *pen; unsigned int pen_only : 1; }; struct TickitRenderBuffer { int lines, cols; // Size RBCell **cells; unsigned int vc_pos_set : 1; int vc_line, vc_col; int xlate_line, xlate_col; TickitRect clip; TickitPen *pen; int depth; RBStack *stack; char *tmp; size_t tmplen; // actually valid size_t tmpsize; // allocated size int refcount; }; static void debug_logf(TickitRenderBuffer *rb, const char *flag, const char *fmt, ...) { va_list args; va_start(args, fmt); char fmt_with_indent[strlen(fmt) + 3 * rb->depth + 1]; { char *s = fmt_with_indent; for(int i = 0; i < rb->depth; i++) s += sprintf(s, "| "); strcpy(s, fmt); } tickit_debug_vlogf(flag, fmt_with_indent, args); va_end(args); } #define DEBUG_LOGF if(tickit_debug_enabled) debug_logf static void free_stack(RBStack *stack) { while(stack) { RBStack *prev = stack->prev; if(stack->pen) tickit_pen_unref(stack->pen); free(stack); stack = prev; } } static void tmp_cat_utf8(TickitRenderBuffer *rb, long codepoint) { int seqlen = tickit_utf8_seqlen(codepoint); if(rb->tmpsize < rb->tmplen + seqlen) { rb->tmpsize *= 2; rb->tmp = realloc(rb->tmp, rb->tmpsize); } tickit_utf8_put(rb->tmp + rb->tmplen, rb->tmpsize - rb->tmplen, codepoint); rb->tmplen += seqlen; /* rb->tmp remains NOT nul-terminated */ } static void tmp_alloc(TickitRenderBuffer *rb, size_t len) { if(rb->tmpsize < len) { free(rb->tmp); while(rb->tmpsize < len) rb->tmpsize *= 2; rb->tmp = malloc(rb->tmpsize); } } static int xlate_and_clip(TickitRenderBuffer *rb, int *line, int *col, int *cols, int *startcol) { *line += rb->xlate_line; *col += rb->xlate_col; const TickitRect *clip = &rb->clip; if(!clip->lines) return 0; if(*line < clip->top || *line >= tickit_rect_bottom(clip) || *col >= tickit_rect_right(clip)) return 0; if(startcol) *startcol = 0; if(*col < clip->left) { *cols -= clip->left - *col; if(startcol) *startcol += clip->left - *col; *col = clip->left; } if(*cols <= 0) return 0; if(*cols > tickit_rect_right(clip) - *col) *cols = tickit_rect_right(clip) - *col; return 1; } static void cont_cell(RBCell *cell, int startcol) { switch(cell->state) { case TEXT: tickit_string_unref(cell->v.text.s); /* fallthrough */ case ERASE: case LINE: case CHAR: tickit_pen_unref(cell->pen); break; case SKIP: case CONT: /* ignore */ break; } cell->state = CONT; cell->maskdepth = -1; cell->startcol = startcol; cell->pen = NULL; } static RBCell *make_span(TickitRenderBuffer *rb, int line, int col, int cols) { int end = col + cols; RBCell **cells = rb->cells; // If the following cell is a CONT, it needs to become a new start if(end < rb->cols && cells[line][end].state == CONT) { int spanstart = cells[line][end].cols; RBCell *spancell = &cells[line][spanstart]; int spanend = spanstart + spancell->startcol; int afterlen = spanend - end; RBCell *endcell = &cells[line][end]; switch(spancell->state) { case SKIP: endcell->state = SKIP; endcell->cols = afterlen; break; case TEXT: endcell->state = TEXT; endcell->cols = afterlen; endcell->pen = tickit_pen_ref(spancell->pen); endcell->v.text.s = tickit_string_ref(spancell->v.text.s); endcell->v.text.offs = spancell->v.text.offs + end - spanstart; break; case ERASE: endcell->state = ERASE; endcell->cols = afterlen; endcell->pen = tickit_pen_ref(spancell->pen); break; case LINE: case CHAR: case CONT: abort(); } // We know these are already CONT cells for(int c = end + 1; c < spanend; c++) cells[line][c].cols = end; } // If the initial cell is a CONT, shorten its start if(cells[line][col].state == CONT) { int beforestart = cells[line][col].cols; RBCell *spancell = &cells[line][beforestart]; int beforelen = col - beforestart; switch(spancell->state) { case SKIP: case TEXT: case ERASE: spancell->cols = beforelen; break; case LINE: case CHAR: case CONT: abort(); } } // cont_cell() also frees any pens in the range for(int c = col; c < end; c++) cont_cell(&cells[line][c], col); cells[line][col].cols = cols; return &cells[line][col]; } // cell creation functions static int put_string(TickitRenderBuffer *rb, int line, int col, TickitString *s) { TickitStringPos endpos; size_t len = tickit_utf8_ncount(tickit_string_get(s), tickit_string_len(s), &endpos, NULL); if(1 + len == 0) return -1; int cols = endpos.columns; int ret = cols; int startcol; if(!xlate_and_clip(rb, &line, &col, &cols, &startcol)) return ret; RBCell *linecells = rb->cells[line]; while(cols) { while(cols && linecells[col].maskdepth > -1) { col++; cols--; startcol++; } if(!cols) break; int spanlen = 0; while(cols && linecells[col + spanlen].maskdepth == -1) { spanlen++; cols--; } if(!spanlen) break; RBCell *cell = make_span(rb, line, col, spanlen); cell->state = TEXT; cell->pen = tickit_pen_ref(rb->pen); cell->v.text.s = tickit_string_ref(s); cell->v.text.offs = startcol; col += spanlen; startcol += spanlen; } return ret; } static int put_text(TickitRenderBuffer *rb, int line, int col, const char *text, size_t len) { TickitString *s = tickit_string_new(text, len == -1 ? strlen(text) : len); int ret = put_string(rb, line, col, s); tickit_string_unref(s); return ret; } static int put_vtextf(TickitRenderBuffer *rb, int line, int col, const char *fmt, va_list args) { /* It's likely the string will fit in, say, 64 bytes */ char buffer[64]; size_t len; { va_list args_for_size; va_copy(args_for_size, args); len = vsnprintf(buffer, sizeof buffer, fmt, args_for_size); va_end(args_for_size); } if(len < sizeof buffer) return put_text(rb, line, col, buffer, len); tmp_alloc(rb, len + 1); vsnprintf(rb->tmp, rb->tmpsize, fmt, args); return put_text(rb, line, col, rb->tmp, len); } static void put_char(TickitRenderBuffer *rb, int line, int col, long codepoint) { int cols = 1; if(!xlate_and_clip(rb, &line, &col, &cols, NULL)) return; if(rb->cells[line][col].maskdepth > -1) return; RBCell *cell = make_span(rb, line, col, cols); cell->state = CHAR; cell->pen = tickit_pen_ref(rb->pen); cell->v.chr.codepoint = codepoint; } static void skip(TickitRenderBuffer *rb, int line, int col, int cols) { if(!xlate_and_clip(rb, &line, &col, &cols, NULL)) return; RBCell *linecells = rb->cells[line]; while(cols) { while(cols && linecells[col].maskdepth > -1) { col++; cols--; } if(!cols) break; int spanlen = 0; while(cols && linecells[col + spanlen].maskdepth == -1) { spanlen++; cols--; } if(!spanlen) break; RBCell *cell = make_span(rb, line, col, spanlen); cell->state = SKIP; col += spanlen; } } static void erase(TickitRenderBuffer *rb, int line, int col, int cols) { if(!xlate_and_clip(rb, &line, &col, &cols, NULL)) return; RBCell *linecells = rb->cells[line]; while(cols) { while(cols && linecells[col].maskdepth > -1) { col++; cols--; } if(!cols) break; int spanlen = 0; while(cols && linecells[col + spanlen].maskdepth == -1) { spanlen++; cols--; } if(!spanlen) break; RBCell *cell = make_span(rb, line, col, spanlen); cell->state = ERASE; cell->pen = tickit_pen_ref(rb->pen); col += spanlen; } } TickitRenderBuffer *tickit_renderbuffer_new(int lines, int cols) { TickitRenderBuffer *rb = malloc(sizeof(TickitRenderBuffer)); rb->lines = lines; rb->cols = cols; rb->cells = malloc(rb->lines * sizeof(RBCell *)); for(int line = 0; line < rb->lines; line++) { rb->cells[line] = malloc(rb->cols * sizeof(RBCell)); rb->cells[line][0].state = SKIP; rb->cells[line][0].maskdepth = -1; rb->cells[line][0].cols = rb->cols; rb->cells[line][0].pen = NULL; for(int col = 1; col < rb->cols; col++) { rb->cells[line][col].state = CONT; rb->cells[line][col].maskdepth = -1; rb->cells[line][col].cols = 0; } } rb->vc_pos_set = 0; rb->xlate_line = 0; rb->xlate_col = 0; tickit_rect_init_sized(&rb->clip, 0, 0, rb->lines, rb->cols); rb->pen = tickit_pen_new(); rb->stack = NULL; rb->depth = 0; rb->tmpsize = 256; // hopefully enough but will grow if required rb->tmp = malloc(rb->tmpsize); rb->tmplen = 0; rb->refcount = 1; return rb; } void tickit_renderbuffer_destroy(TickitRenderBuffer *rb) { for(int line = 0; line < rb->lines; line++) { for(int col = 0; col < rb->cols; col++) { RBCell *cell = &rb->cells[line][col]; switch(cell->state) { case TEXT: tickit_string_unref(cell->v.text.s); /* fallthrough */ case ERASE: case LINE: case CHAR: tickit_pen_unref(cell->pen); break; case SKIP: case CONT: break; } } free(rb->cells[line]); } free(rb->cells); rb->cells = NULL; tickit_pen_unref(rb->pen); if(rb->stack) free_stack(rb->stack); free(rb->tmp); free(rb); } TickitRenderBuffer *tickit_renderbuffer_ref(TickitRenderBuffer *rb) { rb->refcount++; return rb; } void tickit_renderbuffer_unref(TickitRenderBuffer *rb) { if(rb->refcount < 1) { fprintf(stderr, "tickit_renderbuffer_unref: invalid refcount %d\n", rb->refcount); abort(); } rb->refcount--; if(!rb->refcount) tickit_renderbuffer_destroy(rb); } void tickit_renderbuffer_get_size(const TickitRenderBuffer *rb, int *lines, int *cols) { if(lines) *lines = rb->lines; if(cols) *cols = rb->cols; } void tickit_renderbuffer_translate(TickitRenderBuffer *rb, int downward, int rightward) { DEBUG_LOGF(rb, "Bt", "Translate (%+d,%+d)", rightward, downward); rb->xlate_line += downward; rb->xlate_col += rightward; } void tickit_renderbuffer_clip(TickitRenderBuffer *rb, TickitRect *rect) { DEBUG_LOGF(rb, "Bt", "Clip " RECT_PRINTF_FMT, RECT_PRINTF_ARGS(*rect)); TickitRect other; other = *rect; other.top += rb->xlate_line; other.left += rb->xlate_col; if(!tickit_rect_intersect(&rb->clip, &rb->clip, &other)) rb->clip.lines = 0; } void tickit_renderbuffer_mask(TickitRenderBuffer *rb, TickitRect *mask) { DEBUG_LOGF(rb, "Bt", "Mask " RECT_PRINTF_FMT, RECT_PRINTF_ARGS(*mask)); TickitRect hole; hole = *mask; hole.top += rb->xlate_line; hole.left += rb->xlate_col; if(hole.top < 0) { hole.lines += hole.top; hole.top = 0; } if(hole.left < 0) { hole.cols += hole.left; hole.left = 0; } for(int line = hole.top; line < tickit_rect_bottom(&hole) && line < rb->lines; line++) { for(int col = hole.left; col < tickit_rect_right(&hole) && col < rb->cols; col++) { RBCell *cell = &rb->cells[line][col]; if(cell->maskdepth == -1) cell->maskdepth = rb->depth; } } } bool tickit_renderbuffer_has_cursorpos(const TickitRenderBuffer *rb) { return rb->vc_pos_set; } void tickit_renderbuffer_get_cursorpos(const TickitRenderBuffer *rb, int *line, int *col) { if(rb->vc_pos_set && line) *line = rb->vc_line; if(rb->vc_pos_set && col) *col = rb->vc_col; } void tickit_renderbuffer_goto(TickitRenderBuffer *rb, int line, int col) { rb->vc_pos_set = 1; rb->vc_line = line; rb->vc_col = col; } void tickit_renderbuffer_ungoto(TickitRenderBuffer *rb) { rb->vc_pos_set = 0; } void tickit_renderbuffer_setpen(TickitRenderBuffer *rb, const TickitPen *pen) { TickitPen *prevpen = rb->stack ? rb->stack->pen : NULL; /* never mutate the pen inplace; make a new one */ TickitPen *newpen = tickit_pen_new(); if(pen) tickit_pen_copy(newpen, pen, 1); if(prevpen) tickit_pen_copy(newpen, prevpen, 0); tickit_pen_unref(rb->pen); rb->pen = newpen; } void tickit_renderbuffer_reset(TickitRenderBuffer *rb) { for(int line = 0; line < rb->lines; line++) { // cont_cell also frees pen for(int col = 0; col < rb->cols; col++) cont_cell(&rb->cells[line][col], 0); rb->cells[line][0].state = SKIP; rb->cells[line][0].maskdepth = -1; rb->cells[line][0].cols = rb->cols; } rb->vc_pos_set = 0; rb->xlate_line = 0; rb->xlate_col = 0; tickit_rect_init_sized(&rb->clip, 0, 0, rb->lines, rb->cols); tickit_pen_unref(rb->pen); rb->pen = tickit_pen_new(); if(rb->stack) { free_stack(rb->stack); rb->stack = NULL; rb->depth = 0; } } void tickit_renderbuffer_clear(TickitRenderBuffer *rb) { DEBUG_LOGF(rb, "Bd", "Clear"); for(int line = 0; line < rb->lines; line++) erase(rb, line, 0, rb->cols); } void tickit_renderbuffer_save(TickitRenderBuffer *rb) { DEBUG_LOGF(rb, "Bs", "+-Save"); RBStack *stack = malloc(sizeof(struct RBStack)); stack->vc_line = rb->vc_line; stack->vc_col = rb->vc_col; stack->xlate_line = rb->xlate_line; stack->xlate_col = rb->xlate_col; stack->clip = rb->clip; stack->pen = tickit_pen_ref(rb->pen); stack->pen_only = 0; stack->prev = rb->stack; rb->stack = stack; rb->depth++; } void tickit_renderbuffer_savepen(TickitRenderBuffer *rb) { DEBUG_LOGF(rb, "Bs", "+-Savepen"); RBStack *stack = malloc(sizeof(struct RBStack)); stack->pen = tickit_pen_ref(rb->pen); stack->pen_only = 1; stack->prev = rb->stack; rb->stack = stack; rb->depth++; } void tickit_renderbuffer_restore(TickitRenderBuffer *rb) { RBStack *stack; if(!rb->stack) return; stack = rb->stack; rb->stack = stack->prev; if(!stack->pen_only) { rb->vc_line = stack->vc_line; rb->vc_col = stack->vc_col; rb->xlate_line = stack->xlate_line; rb->xlate_col = stack->xlate_col; rb->clip = stack->clip; } tickit_pen_unref(rb->pen); rb->pen = stack->pen; // We've now definitely taken ownership of the old stack frame's pen, so // it doesn't need destroying now rb->depth--; // TODO: this could be done more efficiently by remembering the edges of masking for(int line = 0; line < rb->lines; line++) for(int col = 0; col < rb->cols; col++) if(rb->cells[line][col].maskdepth > rb->depth) rb->cells[line][col].maskdepth = -1; free(stack); DEBUG_LOGF(rb, "Bs", "+-Restore"); } void tickit_renderbuffer_skip_at(TickitRenderBuffer *rb, int line, int col, int cols) { DEBUG_LOGF(rb, "Bd", "Skip (%d..%d,%d)", col, col + cols, line); skip(rb, line, col, cols); } void tickit_renderbuffer_skip(TickitRenderBuffer *rb, int cols) { if(!rb->vc_pos_set) return; DEBUG_LOGF(rb, "Bd", "Skip (%d..%d,%d) +%d", rb->vc_col, rb->vc_col + cols, rb->vc_line, cols); skip(rb, rb->vc_line, rb->vc_col, cols); rb->vc_col += cols; } void tickit_renderbuffer_skip_to(TickitRenderBuffer *rb, int col) { if(!rb->vc_pos_set) return; DEBUG_LOGF(rb, "Bd", "Skip (%d..%d,%d) +%d", rb->vc_col, col, rb->vc_line, col - rb->vc_col); if(rb->vc_col < col) skip(rb, rb->vc_line, rb->vc_col, col - rb->vc_col); rb->vc_col = col; } void tickit_renderbuffer_skiprect(TickitRenderBuffer *rb, TickitRect *rect) { DEBUG_LOGF(rb, "Bd", "Skip [(%d,%d)..(%d,%d)]", rect->left, rect->top, tickit_rect_right(rect), tickit_rect_bottom(rect)); for(int line = rect->top; line < tickit_rect_bottom(rect); line++) skip(rb, line, rect->left, rect->cols); } int tickit_renderbuffer_text_at(TickitRenderBuffer *rb, int line, int col, const char *text) { return tickit_renderbuffer_textn_at(rb, line, col, text, -1); } int tickit_renderbuffer_textn_at(TickitRenderBuffer *rb, int line, int col, const char *text, size_t len) { int cols = put_text(rb, line, col, text, len); DEBUG_LOGF(rb, "Bd", "Text (%d..%d,%d)", col, col + cols, line); return cols; } int tickit_renderbuffer_text(TickitRenderBuffer *rb, const char *text) { return tickit_renderbuffer_textn(rb, text, -1); } int tickit_renderbuffer_textn(TickitRenderBuffer *rb, const char *text, size_t len) { if(!rb->vc_pos_set) return -1; int cols = put_text(rb, rb->vc_line, rb->vc_col, text, len); DEBUG_LOGF(rb, "Bd", "Text (%d..%d,%d) +%d", rb->vc_col, rb->vc_col + cols, rb->vc_line, cols); rb->vc_col += cols; return cols; } int tickit_renderbuffer_textf_at(TickitRenderBuffer *rb, int line, int col, const char *fmt, ...) { va_list args; va_start(args, fmt); int ret = tickit_renderbuffer_vtextf_at(rb, line, col, fmt, args); va_end(args); return ret; } int tickit_renderbuffer_vtextf_at(TickitRenderBuffer *rb, int line, int col, const char *fmt, va_list args) { int cols = put_vtextf(rb, line, col, fmt, args); DEBUG_LOGF(rb, "Bd", "Text (%d..%d,%d)", col, col + cols, line); return cols; } int tickit_renderbuffer_textf(TickitRenderBuffer *rb, const char *fmt, ...) { va_list args; va_start(args, fmt); int ret = tickit_renderbuffer_vtextf(rb, fmt, args); va_end(args); return ret; } int tickit_renderbuffer_vtextf(TickitRenderBuffer *rb, const char *fmt, va_list args) { if(!rb->vc_pos_set) return -1; int cols = put_vtextf(rb, rb->vc_line, rb->vc_col, fmt, args); DEBUG_LOGF(rb, "Bd", "Text (%d..%d,%d) +%d", rb->vc_col, rb->vc_col + cols, rb->vc_line, cols); rb->vc_col += cols; return cols; } void tickit_renderbuffer_erase_at(TickitRenderBuffer *rb, int line, int col, int cols) { DEBUG_LOGF(rb, "Bd", "Erase (%d..%d,%d)", col, col + cols, line); erase(rb, line, col, cols); } void tickit_renderbuffer_erase(TickitRenderBuffer *rb, int cols) { if(!rb->vc_pos_set) return; DEBUG_LOGF(rb, "Bd", "Erase (%d..%d,%d) +%d", rb->vc_col, rb->vc_col + cols, rb->vc_line, cols); erase(rb, rb->vc_line, rb->vc_col, cols); rb->vc_col += cols; } void tickit_renderbuffer_erase_to(TickitRenderBuffer *rb, int col) { if(!rb->vc_pos_set) return; DEBUG_LOGF(rb, "Bd", "Erase (%d..%d,%d) +%d", rb->vc_col, col, rb->vc_line, col - rb->vc_col); if(rb->vc_col < col) erase(rb, rb->vc_line, rb->vc_col, col - rb->vc_col); rb->vc_col = col; } void tickit_renderbuffer_eraserect(TickitRenderBuffer *rb, TickitRect *rect) { DEBUG_LOGF(rb, "Bd", "Erase [(%d,%d)..(%d,%d)]", rect->left, rect->top, tickit_rect_right(rect), tickit_rect_bottom(rect)); for(int line = rect->top; line < tickit_rect_bottom(rect); line++) erase(rb, line, rect->left, rect->cols); } void tickit_renderbuffer_char_at(TickitRenderBuffer *rb, int line, int col, long codepoint) { DEBUG_LOGF(rb, "Bd", "Char (%d.,%d,%d)", col, col + 1, line); put_char(rb, line, col, codepoint); } void tickit_renderbuffer_char(TickitRenderBuffer *rb, long codepoint) { if(!rb->vc_pos_set) return; DEBUG_LOGF(rb, "Bd", "Char (%d..%d,%d) +%d", rb->vc_col, rb->vc_col + 1, rb->vc_line, 1); put_char(rb, rb->vc_line, rb->vc_col, codepoint); // TODO: might not be 1; would have to look it up rb->vc_col += 1; } static void linecell(TickitRenderBuffer *rb, int line, int col, int bits) { int cols = 1; if(!xlate_and_clip(rb, &line, &col, &cols, NULL)) return; if(rb->cells[line][col].maskdepth > -1) return; RBCell *cell = &rb->cells[line][col]; if(cell->state != LINE) { make_span(rb, line, col, cols); cell->state = LINE; cell->cols = 1; cell->pen = tickit_pen_ref(rb->pen); cell->v.line.mask = 0; } else if(!tickit_pen_equiv(cell->pen, rb->pen)) { tickit_pen_unref(cell->pen); cell->pen = tickit_pen_ref(rb->pen); } cell->v.line.mask |= bits; } void tickit_renderbuffer_hline_at(TickitRenderBuffer *rb, int line, int startcol, int endcol, TickitLineStyle style, TickitLineCaps caps) { DEBUG_LOGF(rb, "Bd", "HLine (%d..%d,%d)", startcol, endcol, line); int east = style << EAST_SHIFT; int west = style << WEST_SHIFT; linecell(rb, line, startcol, east | (caps & TICKIT_LINECAP_START ? west : 0)); for(int col = startcol + 1; col <= endcol - 1; col++) linecell(rb, line, col, east | west); linecell(rb, line, endcol, (caps & TICKIT_LINECAP_END ? east : 0) | west); } void tickit_renderbuffer_vline_at(TickitRenderBuffer *rb, int startline, int endline, int col, TickitLineStyle style, TickitLineCaps caps) { DEBUG_LOGF(rb, "Bd", "VLine (%d,%d..%d)", col, startline, endline); int north = style << NORTH_SHIFT; int south = style << SOUTH_SHIFT; linecell(rb, startline, col, south | (caps & TICKIT_LINECAP_START ? north : 0)); for(int line = startline + 1; line <= endline - 1; line++) linecell(rb, line, col, south | north); linecell(rb, endline, col, (caps & TICKIT_LINECAP_END ? south : 0) | north); } void tickit_renderbuffer_flush_to_term(TickitRenderBuffer *rb, TickitTerm *tt) { DEBUG_LOGF(rb, "Bf", "Flush to term"); for(int line = 0; line < rb->lines; line++) { int phycol = -1; /* column where the terminal cursor physically is */ for(int col = 0; col < rb->cols; /**/) { RBCell *cell = &rb->cells[line][col]; if(cell->state == SKIP) { col += cell->cols; continue; } if(phycol < col) tickit_term_goto(tt, line, col); phycol = col; switch(cell->state) { case TEXT: { TickitStringPos start, end, limit; const char *text = tickit_string_get(cell->v.text.s); tickit_stringpos_limit_columns(&limit, cell->v.text.offs); tickit_utf8_count(text, &start, &limit); limit.columns += cell->cols; end = start; tickit_utf8_countmore(text, &end, &limit); tickit_term_setpen(tt, cell->pen); tickit_term_printn(tt, text + start.bytes, end.bytes - start.bytes); phycol += cell->cols; } break; case ERASE: { /* No need to set moveend=true to erasech unless we actually * have more content */ int moveend = col + cell->cols < rb->cols && rb->cells[line][col + cell->cols].state != SKIP; tickit_term_setpen(tt, cell->pen); tickit_term_erasech(tt, cell->cols, moveend ? TICKIT_YES : TICKIT_MAYBE); if(moveend) phycol += cell->cols; else phycol = -1; } break; case LINE: { TickitPen *pen = cell->pen; do { tmp_cat_utf8(rb, linemask_to_char[cell->v.line.mask]); col++; phycol += cell->cols; } while(col < rb->cols && (cell = &rb->cells[line][col]) && cell->state == LINE && tickit_pen_equiv(cell->pen, pen)); tickit_term_setpen(tt, pen); tickit_term_printn(tt, rb->tmp, rb->tmplen); rb->tmplen = 0; } continue; /* col already updated */ case CHAR: { tmp_cat_utf8(rb, cell->v.chr.codepoint); tickit_term_setpen(tt, cell->pen); tickit_term_printn(tt, rb->tmp, rb->tmplen); rb->tmplen = 0; phycol += cell->cols; } break; case SKIP: case CONT: /* unreachable */ abort(); } col += cell->cols; } } tickit_renderbuffer_reset(rb); } static void copyrect(TickitRenderBuffer *dst, TickitRenderBuffer *src, const TickitRect *dstrect, const TickitRect *srcrect, bool copy_skip) { if(srcrect->lines == 0 || srcrect->cols == 0) return; /* TODO: * * consider how this works in the presence of a translation offset * defined on src */ int lineoffs = dstrect->top - srcrect->top, coloffs = dstrect->left - srcrect->left; int bottom = tickit_rect_bottom(srcrect), right = tickit_rect_right(srcrect); /* Several steps have to be done somewhat specially for copies into the same * RB */ bool samerb = dst == src; if(samerb && lineoffs == 0 && coloffs == 0) return; /* iterate lines from the bottom upward if we're coping down in the same RB */ bool upwards = samerb && (lineoffs > 0); /* iterate columns leftward if we're copying rightward in the same RB */ bool leftwards = samerb && (lineoffs == 0) && (coloffs > 0); for(int line = upwards ? bottom - 1 : srcrect->top; upwards ? line >= srcrect->top : line < bottom; upwards ? line-- : line++) { for(int col = leftwards ? right - 1 : srcrect->left; leftwards ? col >= srcrect->left : col < right; /**/) { RBCell *cell = &src->cells[line][col]; int offset = 0; if(cell->state == CONT) { int startcol = cell->startcol; cell = &src->cells[line][startcol]; if(leftwards) { col = startcol; if(col < srcrect->left) col = srcrect->left; } offset = col - startcol; } int cols = cell->cols; if(col + cols > tickit_rect_right(srcrect)) cols = tickit_rect_right(srcrect) - col; if(cell->state != SKIP) { tickit_renderbuffer_savepen(dst); tickit_renderbuffer_setpen(dst, cell->pen); } switch(cell->state) { case SKIP: if(copy_skip) skip(dst, line + lineoffs, col + coloffs, cols); break; case TEXT: { TickitStringPos start, end, limit; const char *text = tickit_string_get(cell->v.text.s); tickit_stringpos_limit_columns(&limit, cell->v.text.offs + offset); tickit_utf8_count(text, &start, &limit); limit.columns += cols; end = start; tickit_utf8_countmore(text, &end, &limit); if(start.bytes > 0 || end.bytes < tickit_string_len(cell->v.text.s)) put_text(dst, line + lineoffs, col + coloffs, text + start.bytes, end.bytes - start.bytes); else // We can just cheaply copy the entire string put_string(dst, line + lineoffs, col + coloffs, cell->v.text.s); } break; case ERASE: erase(dst, line + lineoffs, col + coloffs, cols); break; case LINE: linecell(dst, line + lineoffs, col + coloffs, cell->v.line.mask); break; case CHAR: put_char(dst, line + lineoffs, col + coloffs, cell->v.chr.codepoint); break; case CONT: /* unreachable */ abort(); } if(cell->state != SKIP) tickit_renderbuffer_restore(dst); if(leftwards) col--; /* we'll jump back to the beginning of a CONT region on the next iteration */ else col += cell->cols; } } } void tickit_renderbuffer_blit(TickitRenderBuffer *dst, TickitRenderBuffer *src) { copyrect(dst, src, &(TickitRect){ .top = 0, .left = 0, .lines = src->lines, .cols = src->cols }, &(TickitRect){ .top = 0, .left = 0, .lines = src->lines, .cols = src->cols }, false); } void tickit_renderbuffer_copyrect(TickitRenderBuffer *rb, const TickitRect *dest, const TickitRect *src) { copyrect(rb, rb, dest, src, true); } void tickit_renderbuffer_moverect(TickitRenderBuffer *rb, const TickitRect *dest, const TickitRect *src) { copyrect(rb, rb, dest, src, true); /* Calculate what area of the RB needs skipping due to move */ TickitRectSet *cleararea = tickit_rectset_new(); tickit_rectset_add(cleararea, src); tickit_rectset_subtract(cleararea, &(TickitRect){ .top = dest->top, .left = dest->left, .lines = src->lines, .cols = src->cols}); size_t n = tickit_rectset_rects(cleararea); for(size_t i = 0; i < n; i++) { TickitRect rect; tickit_rectset_get_rect(cleararea, i, &rect); tickit_renderbuffer_skiprect(rb, &rect); } tickit_rectset_destroy(cleararea); } static RBCell *get_span(TickitRenderBuffer *rb, int line, int col, int *offset) { int cols = 1; if(!xlate_and_clip(rb, &line, &col, &cols, NULL)) return NULL; *offset = 0; RBCell *cell = &rb->cells[line][col]; if(cell->state == CONT) { *offset = col - cell->startcol; cell = &rb->cells[line][cell->startcol]; } return cell; } static size_t get_span_text(TickitRenderBuffer *rb, RBCell *span, int offset, int one_grapheme, char *buffer, size_t len) { size_t bytes; switch(span->state) { case CONT: // should be unreachable return -1; case SKIP: case ERASE: bytes = 0; break; case TEXT: { const char *text = tickit_string_get(span->v.text.s); TickitStringPos start, end, limit; tickit_stringpos_limit_columns(&limit, span->v.text.offs + offset); tickit_utf8_count(text, &start, &limit); if(one_grapheme) tickit_stringpos_limit_graphemes(&limit, start.graphemes + 1); else tickit_stringpos_limit_columns(&limit, span->cols); end = start; tickit_utf8_countmore(text, &end, &limit); bytes = end.bytes - start.bytes; if(buffer) { if(len < bytes) return -1; strncpy(buffer, text + start.bytes, bytes); buffer[bytes] = 0; } break; } case LINE: bytes = tickit_utf8_put(buffer, len, linemask_to_char[span->v.line.mask]); break; case CHAR: bytes = tickit_utf8_put(buffer, len, span->v.chr.codepoint); break; } if(buffer && len > bytes) buffer[bytes] = 0; return bytes; } int tickit_renderbuffer_get_cell_active(TickitRenderBuffer *rb, int line, int col) { int offset; RBCell *span = get_span(rb, line, col, &offset); if(!span) return -1; return span->state != SKIP; } size_t tickit_renderbuffer_get_cell_text(TickitRenderBuffer *rb, int line, int col, char *buffer, size_t len) { int offset; RBCell *span = get_span(rb, line, col, &offset); if(!span || span->state == CONT) return -1; return get_span_text(rb, span, offset, 1, buffer, len); } TickitRenderBufferLineMask tickit_renderbuffer_get_cell_linemask(TickitRenderBuffer *rb, int line, int col) { int offset; RBCell *span = get_span(rb, line, col, &offset); if(!span || span->state != LINE) return (TickitRenderBufferLineMask){ 0 }; return (TickitRenderBufferLineMask){ .north = (span->v.line.mask >> NORTH_SHIFT) & 0x03, .south = (span->v.line.mask >> SOUTH_SHIFT) & 0x03, .east = (span->v.line.mask >> EAST_SHIFT ) & 0x03, .west = (span->v.line.mask >> WEST_SHIFT ) & 0x03, }; } TickitPen *tickit_renderbuffer_get_cell_pen(TickitRenderBuffer *rb, int line, int col) { int offset; RBCell *span = get_span(rb, line, col, &offset); if(!span || span->state == SKIP) return NULL; return span->pen; } size_t tickit_renderbuffer_get_span(TickitRenderBuffer *rb, int line, int startcol, struct TickitRenderBufferSpanInfo *info, char *text, size_t len) { int offset; RBCell *span = get_span(rb, line, startcol, &offset); if(!span || span->state == CONT) return -1; if(info) info->n_columns = span->cols - offset; if(span->state == SKIP) { if(info) info->is_active = 0; return 0; } if(info) info->is_active = 1; if(info && info->pen) { tickit_pen_clear(info->pen); tickit_pen_copy(info->pen, span->pen, 1); } size_t retlen = get_span_text(rb, span, offset, 0, text, len); if(info) { info->len = retlen; info->text = text; } return len; } libtickit-0.3.4/src/string.c0000644000000000000000000000132213613430267014066 0ustar 00000000000000#include "tickit.h" #include struct TickitString { int refcount; size_t len; char str[0]; }; TickitString *tickit_string_new(const char *str, size_t len) { TickitString *s = malloc(sizeof(TickitString) + len + 1); s->refcount = 1; s->len = len; memcpy(s->str, str, len); s->str[len] = '\0'; return s; } TickitString *tickit_string_ref(TickitString *s) { s->refcount++; return s; } void tickit_string_unref(TickitString *s) { if(s->refcount > 1) { s->refcount--; return; } s->refcount = 0; s->str[0] = '\0'; free(s); } const char *tickit_string_get(const TickitString *s) { return s->str; } size_t tickit_string_len(const TickitString *s) { return s->len; } libtickit-0.3.4/src/term.c0000644000000000000000000006142513613430267013541 0ustar 00000000000000/* We need C99 vsnprintf() semantics */ #ifdef __GLIBC__ /* We need C99 vsnprintf() semantics */ # define _ISOC99_SOURCE /* We need sigaction() and struct sigaction */ # define _POSIX_C_SOURCE 199309L /* We need strdup and va_copy */ # define _GNU_SOURCE #endif #include "tickit.h" #include "bindings.h" #include "termdriver.h" #include "xterm-palette.inc" #include #include #include #include #include #include #include #include #include #include #include #define streq(a,b) (!strcmp(a,b)) /* unit multipliers for working in microseconds */ #define MSEC 1000 #define SECOND 1000000 #include static TickitTermDriverProbe *driver_probes[] = { &tickit_termdrv_probe_xterm, &tickit_termdrv_probe_ti, NULL, }; struct TickitTerm { int outfd; TickitTermOutputFunc *outfunc; void *outfunc_user; int infd; TermKey *termkey; struct timeval input_timeout_at; /* absolute time */ struct TickitTerminfoHook ti_hook; char *termtype; TickitMaybeBool is_utf8; char *outbuffer; size_t outbuffer_len; /* size of outbuffer */ size_t outbuffer_cur; /* current fill level */ char *tmpbuffer; size_t tmpbuffer_len; TickitTermDriver *driver; int lines; int cols; bool observe_winch; TickitTerm *next_sigwinch_observer; bool window_changed; enum { UNSTARTED, STARTING, STARTED } state; int colors; TickitPen *pen; int refcount; struct TickitBindings bindings; int mouse_buttons_held; }; DEFINE_BINDINGS_FUNCS(term,TickitTerm,TickitTermEventFn) static void *get_tmpbuffer(TickitTerm *tt, size_t len); static const char *getstr_hook(const char *name, const char *value, void *_tt) { TickitTerm *tt = _tt; if(streq(name, "key_backspace")) { /* Many terminfos lie about backspace. Rather than trust it even a little * tiny smidge, we'll interrogate what termios thinks of the VERASE char * and claim that is the backspace key. It's what neovim does * * https://github.com/neovim/neovim/blob/1083c626b9a3fc858c552d38250c3c555cda4074/src/nvim/tui/tui.c#L1982 */ struct termios termios; tcgetattr(tt->infd, &termios); char *ret = get_tmpbuffer(tt, 2); ret[0] = termios.c_cc[VERASE]; ret[1] = 0; value = ret; } if(tt->ti_hook.getstr) value = (*tt->ti_hook.getstr)(name, value, tt->ti_hook.data); return value; } static TermKey *get_termkey(TickitTerm *tt) { if(!tt->termkey) { int flags = 0; if(tt->is_utf8 == TICKIT_YES) flags |= TERMKEY_FLAG_UTF8; else if(tt->is_utf8 == TICKIT_NO) flags |= TERMKEY_FLAG_RAW; tt->termkey = termkey_new(tt->infd, TERMKEY_FLAG_EINTR|TERMKEY_FLAG_NOSTART | flags); termkey_hook_terminfo_getstr(tt->termkey, getstr_hook, tt); termkey_start(tt->termkey); tt->is_utf8 = !!(termkey_get_flags(tt->termkey) & TERMKEY_FLAG_UTF8); } termkey_set_canonflags(tt->termkey, termkey_get_canonflags(tt->termkey) | TERMKEY_CANON_DELBS); return tt->termkey; } static TickitTermDriver *tickit_term_build_driver(struct TickitTermBuilder *builder) { if(builder->driver) return builder->driver; TickitTermProbeArgs args = { .termtype = builder->termtype, .ti_hook = builder->ti_hook, }; for(int i = 0; driver_probes[i]; i++) { TickitTermDriver *driver = (*driver_probes[i]->new)(&args); if(driver) return driver; } errno = ENOENT; return NULL; } TickitTerm *tickit_term_build(const struct TickitTermBuilder *_builder) { struct TickitTermBuilder builder = { 0 }; if(_builder) builder = *_builder; if(!builder.termtype) builder.termtype = getenv("TERM"); if(!builder.termtype) builder.termtype = "xterm"; TickitTerm *tt = malloc(sizeof(TickitTerm)); if(!tt) return NULL; if(builder.ti_hook) tt->ti_hook = *builder.ti_hook; else tt->ti_hook = (struct TickitTerminfoHook){ 0 }; TickitTermDriver *driver = tickit_term_build_driver(&builder); if(!driver) return NULL; tt->outfd = -1; tt->outfunc = NULL; tt->infd = -1; tt->termkey = NULL; tt->input_timeout_at.tv_sec = -1; tt->outbuffer = NULL; tt->outbuffer_len = 0; tt->outbuffer_cur = 0; tt->tmpbuffer = NULL; tt->tmpbuffer_len = 0; tt->is_utf8 = TICKIT_MAYBE; /* Initially; the driver may provide a more accurate value */ tt->lines = 25; tt->cols = 80; tt->observe_winch = false; tt->next_sigwinch_observer = NULL; tt->window_changed = false; tt->refcount = 1; tt->bindings = (struct TickitBindings){ NULL }; tt->mouse_buttons_held = 0; /* Initially empty because we don't necessarily know the initial state * of the terminal */ tt->pen = tickit_pen_new(); if(builder.termtype) tt->termtype = strdup(builder.termtype); else tt->termtype = NULL; tt->driver = driver; tt->driver->tt = tt; if(tt->driver->vtable->attach) (*tt->driver->vtable->attach)(tt->driver, tt); tickit_term_getctl_int(tt, TICKIT_TERMCTL_COLORS, &tt->colors); // Can't 'start' yet until we have an output method tt->state = UNSTARTED; return tt; } TickitTerm *tickit_term_new(void) { return tickit_term_build(NULL); } TickitTerm *tickit_term_new_for_termtype(const char *termtype) { return tickit_term_build(&(struct TickitTermBuilder){ .termtype = termtype, }); } TickitTerm *tickit_term_new_for_driver(TickitTermDriver *ttd) { return tickit_term_build(&(struct TickitTermBuilder){ .driver = ttd, }); } TickitTerm *tickit_term_open_stdio(void) { TickitTerm *tt = tickit_term_new(); if(!tt) return NULL; tickit_term_set_input_fd(tt, STDIN_FILENO); tickit_term_set_output_fd(tt, STDOUT_FILENO); tickit_term_observe_sigwinch(tt, true); return tt; } void tickit_term_destroy(TickitTerm *tt) { if(tt->observe_winch) tickit_term_observe_sigwinch(tt, false); if(tt->driver) { if(tt->driver->vtable->stop) (*tt->driver->vtable->stop)(tt->driver); (*tt->driver->vtable->destroy)(tt->driver); } tickit_term_flush(tt); if(tt->outfunc) (*tt->outfunc)(tt, NULL, 0, tt->outfunc_user); tickit_bindings_unbind_and_destroy(&tt->bindings, tt); tickit_pen_unref(tt->pen); if(tt->termkey) termkey_destroy(tt->termkey); if(tt->outbuffer) free(tt->outbuffer); if(tt->tmpbuffer) free(tt->tmpbuffer); if(tt->termtype) free(tt->termtype); free(tt); } TickitTerm *tickit_term_ref(TickitTerm *tt) { tt->refcount++; return tt; } void tickit_term_unref(TickitTerm *tt) { if(tt->refcount < 1) { fprintf(stderr, "tickit_term_unref: invalid refcount %d\n", tt->refcount); abort(); } tt->refcount--; if(!tt->refcount) tickit_term_destroy(tt); } const char *tickit_term_get_termtype(TickitTerm *tt) { return tt->termtype; } TickitTermDriver *tickit_term_get_driver(TickitTerm *tt) { return tt->driver; } static void *get_tmpbuffer(TickitTerm *tt, size_t len) { if(tt->tmpbuffer_len < len) { if(tt->tmpbuffer) free(tt->tmpbuffer); tt->tmpbuffer = malloc(len); tt->tmpbuffer_len = len; } return tt->tmpbuffer; } /* Driver API */ void *tickit_termdrv_get_tmpbuffer(TickitTermDriver *ttd, size_t len) { return get_tmpbuffer(ttd->tt, len); } void tickit_term_get_size(const TickitTerm *tt, int *lines, int *cols) { if(lines) *lines = tt->lines; if(cols) *cols = tt->cols; } void tickit_term_set_size(TickitTerm *tt, int lines, int cols) { if(tt->lines != lines || tt->cols != cols) { tt->lines = lines; tt->cols = cols; TickitResizeEventInfo info = { .lines = lines, .cols = cols }; run_events(tt, TICKIT_TERM_ON_RESIZE, &info); } } void tickit_term_refresh_size(TickitTerm *tt) { if(tt->outfd == -1) return; struct winsize ws = { 0, 0, 0, 0 }; if(ioctl(tt->outfd, TIOCGWINSZ, &ws) == -1) return; tickit_term_set_size(tt, ws.ws_row, ws.ws_col); } static TickitTerm *first_sigwinch_observer; static void sigwinch(int signum) { for(TickitTerm *tt = first_sigwinch_observer; tt; tt = tt->next_sigwinch_observer) tt->window_changed = 1; } void tickit_term_observe_sigwinch(TickitTerm *tt, bool observe) { sigset_t newset; sigset_t oldset; sigemptyset(&newset); sigaddset(&newset, SIGWINCH); sigprocmask(SIG_BLOCK, &newset, &oldset); if(observe && !tt->observe_winch) { tt->observe_winch = true; if(!first_sigwinch_observer) sigaction(SIGWINCH, &(struct sigaction){ .sa_handler = sigwinch }, NULL); TickitTerm **tailp = &first_sigwinch_observer; while(*tailp) tailp = &(*tailp)->next_sigwinch_observer; *tailp = tt; } else if(!observe && tt->observe_winch) { TickitTerm **tailp = &first_sigwinch_observer; while(tailp && *tailp != tt) tailp = &(*tailp)->next_sigwinch_observer; if(tailp) *tailp = (*tailp)->next_sigwinch_observer; if(!first_sigwinch_observer) sigaction(SIGWINCH, &(struct sigaction){ .sa_handler = SIG_DFL }, NULL); tt->observe_winch = false; } sigprocmask(SIG_SETMASK, &oldset, NULL); } static void check_resize(TickitTerm *tt) { if(!tt->window_changed) return; tt->window_changed = 0; tickit_term_refresh_size(tt); } void tickit_term_set_output_fd(TickitTerm *tt, int fd) { tt->outfd = fd; tickit_term_refresh_size(tt); if(tt->state == UNSTARTED) { if(tt->driver->vtable->start) (*tt->driver->vtable->start)(tt->driver); tt->state = STARTING; } } int tickit_term_get_output_fd(const TickitTerm *tt) { return tt->outfd; } void tickit_term_set_output_func(TickitTerm *tt, TickitTermOutputFunc *fn, void *user) { if(tt->outfunc) (*tt->outfunc)(tt, NULL, 0, tt->outfunc_user); tt->outfunc = fn; tt->outfunc_user = user; if(tt->state == UNSTARTED) { if(tt->driver->vtable->start) (*tt->driver->vtable->start)(tt->driver); tt->state = STARTING; } } void tickit_term_set_output_buffer(TickitTerm *tt, size_t len) { void *buffer = len ? malloc(len) : NULL; if(tt->outbuffer) free(tt->outbuffer); tt->outbuffer = buffer; tt->outbuffer_len = len; tt->outbuffer_cur = 0; } void tickit_term_set_input_fd(TickitTerm *tt, int fd) { if(tt->termkey) termkey_destroy(tt->termkey); tt->infd = fd; (void)get_termkey(tt); } int tickit_term_get_input_fd(const TickitTerm *tt) { return tt->infd; } TickitMaybeBool tickit_term_get_utf8(const TickitTerm *tt) { return tt->is_utf8; } void tickit_term_set_utf8(TickitTerm *tt, bool utf8) { tt->is_utf8 = !!utf8; /* TODO: See what we can think of for the output side */ if(tt->termkey) { int flags = termkey_get_flags(tt->termkey) & ~(TERMKEY_FLAG_UTF8|TERMKEY_FLAG_RAW); if(utf8) flags |= TERMKEY_FLAG_UTF8; else flags |= TERMKEY_FLAG_RAW; termkey_set_flags(tt->termkey, flags); } } void tickit_term_await_started_msec(TickitTerm *tt, long msec) { if(msec > -1) tickit_term_await_started_tv(tt, &(struct timeval){ .tv_sec = msec / 1000, .tv_usec = (msec % 1000) * 1000, }); else tickit_term_await_started_tv(tt, NULL); } void tickit_term_await_started_tv(TickitTerm *tt, const struct timeval *timeout) { if(tt->state == STARTED) return; struct timeval until; gettimeofday(&until, NULL); // until += timeout if(until.tv_usec + timeout->tv_usec >= 1E6) { until.tv_sec += timeout->tv_sec + 1; until.tv_usec += timeout->tv_usec - 1E6; } else { until.tv_sec += timeout->tv_sec; until.tv_usec += timeout->tv_usec; } while(tt->state != STARTED) { if(!tt->driver->vtable->started || (*tt->driver->vtable->started)(tt->driver)) break; struct timeval timeout; gettimeofday(&timeout, NULL); // timeout = until - timeout if(until.tv_usec < timeout.tv_usec) { timeout.tv_sec = until.tv_sec - timeout.tv_sec - 1; timeout.tv_usec = until.tv_usec - timeout.tv_usec + 1E6; } else { timeout.tv_sec = until.tv_sec - timeout.tv_sec; timeout.tv_usec = until.tv_usec - timeout.tv_usec; } if(timeout.tv_sec < 0) break; tickit_term_input_wait_tv(tt, &timeout); } tt->state = STARTED; } static void got_key(TickitTerm *tt, TermKey *tk, TermKeyKey *key) { if(tt->driver->vtable->gotkey && (*tt->driver->vtable->gotkey)(tt->driver, tk, key)) return; if(key->type == TERMKEY_TYPE_MOUSE) { TermKeyMouseEvent ev; TickitMouseEventInfo info; termkey_interpret_mouse(tk, key, &ev, &info.button, &info.line, &info.col); /* TermKey is 1-based, Tickit is 0-based for position */ info.line--; info.col--; switch(ev) { case TERMKEY_MOUSE_PRESS: info.type = TICKIT_MOUSEEV_PRESS; break; case TERMKEY_MOUSE_DRAG: info.type = TICKIT_MOUSEEV_DRAG; break; case TERMKEY_MOUSE_RELEASE: info.type = TICKIT_MOUSEEV_RELEASE; break; default: info.type = -1; break; } /* Translate PRESS of buttons >= 4 into wheel events */ if(ev == TERMKEY_MOUSE_PRESS && info.button >= 4) { info.type = TICKIT_MOUSEEV_WHEEL; info.button -= (4 - TICKIT_MOUSEWHEEL_UP); } info.mod = key->modifiers; if(info.type == TICKIT_MOUSEEV_PRESS || info.type == TICKIT_MOUSEEV_DRAG) { tt->mouse_buttons_held |= (1 << info.button); } else if(info.type == TICKIT_MOUSEEV_RELEASE && info.button) { tt->mouse_buttons_held &= ~(1 << info.button); } else if(info.type == TICKIT_MOUSEEV_RELEASE) { /* X10 cannot report which button was released. Just report that they * all were */ for(info.button = 1; tt->mouse_buttons_held; info.button++) if(tt->mouse_buttons_held & (1 << info.button)) { run_events_whilefalse(tt, TICKIT_TERM_ON_MOUSE, &info); tt->mouse_buttons_held &= ~(1 << info.button); } return; // Buttons have been handled } run_events_whilefalse(tt, TICKIT_TERM_ON_MOUSE, &info); } else if(key->type == TERMKEY_TYPE_UNICODE && !key->modifiers) { /* Unmodified unicode */ TickitKeyEventInfo info = { .type = TICKIT_KEYEV_TEXT, .str = key->utf8, .mod = key->modifiers, }; run_events_whilefalse(tt, TICKIT_TERM_ON_KEY, &info); } else if(key->type == TERMKEY_TYPE_UNICODE || key->type == TERMKEY_TYPE_FUNCTION || key->type == TERMKEY_TYPE_KEYSYM) { char buffer[64]; // TODO: should be long enough termkey_strfkey(tk, buffer, sizeof buffer, key, TERMKEY_FORMAT_ALTISMETA); TickitKeyEventInfo info = { .type = TICKIT_KEYEV_KEY, .str = buffer, .mod = key->modifiers, }; run_events_whilefalse(tt, TICKIT_TERM_ON_KEY, &info); } } void tickit_term_emit_key(TickitTerm *tt, TickitKeyEventInfo *info) { run_events_whilefalse(tt, TICKIT_TERM_ON_KEY, info); } void tickit_term_emit_mouse(TickitTerm *tt, TickitMouseEventInfo *info) { run_events_whilefalse(tt, TICKIT_TERM_ON_MOUSE, info); } static void get_keys(TickitTerm *tt, TermKey *tk) { TermKeyResult res; TermKeyKey key; while((res = termkey_getkey(tk, &key)) == TERMKEY_RES_KEY) { got_key(tt, tk, &key); } if(res == TERMKEY_RES_AGAIN) { struct timeval tv; gettimeofday(&tv, NULL); /* tv += waittime in MSEC */ int new_usec = tv.tv_usec + (termkey_get_waittime(tk) * MSEC); if(new_usec >= SECOND) { tv.tv_sec++; new_usec -= SECOND; } tv.tv_usec = new_usec; tt->input_timeout_at = tv; } else { tt->input_timeout_at.tv_sec = -1; } } void tickit_term_input_push_bytes(TickitTerm *tt, const char *bytes, size_t len) { check_resize(tt); TermKey *tk = get_termkey(tt); termkey_push_bytes(tk, bytes, len); get_keys(tt, tk); } void tickit_term_input_readable(TickitTerm *tt) { check_resize(tt); TermKey *tk = get_termkey(tt); termkey_advisereadable(tk); get_keys(tt, tk); } static int get_timeout(TickitTerm *tt) { if(tt->input_timeout_at.tv_sec == -1) return -1; struct timeval tv; gettimeofday(&tv, NULL); /* tv = tt->input_timeout_at - tv */ int new_usec = tt->input_timeout_at.tv_usec - tv.tv_usec; tv.tv_sec = tt->input_timeout_at.tv_sec - tv.tv_sec; if(new_usec < 0) { tv.tv_sec--; tv.tv_usec = new_usec + SECOND; } else { tv.tv_usec = new_usec; } if(tv.tv_sec > 0 || (tv.tv_sec == 0 && tv.tv_usec > 0)) return tv.tv_sec * 1000 + (tv.tv_usec+MSEC-1)/MSEC; return 0; } static void timedout(TickitTerm *tt) { TermKey *tk = get_termkey(tt); TermKeyKey key; if(termkey_getkey_force(tk, &key) == TERMKEY_RES_KEY) { got_key(tt, tk, &key); } tt->input_timeout_at.tv_sec = -1; } int tickit_term_input_check_timeout_msec(TickitTerm *tt) { check_resize(tt); int msec = get_timeout(tt); if(msec != 0) return msec; timedout(tt); return -1; } void tickit_term_input_wait_msec(TickitTerm *tt, long msec) { TermKey *tk = get_termkey(tt); int maxwait = get_timeout(tt); if(maxwait > -1) { if(msec == -1 || maxwait < msec) msec = maxwait; } struct timeval timeout; if(msec > -1) { timeout.tv_sec = msec / 1000; timeout.tv_usec = (msec % 1000) * 1000; } fd_set readfds; FD_ZERO(&readfds); int fd = termkey_get_fd(tk); if (fd < 0 || fd >= FD_SETSIZE) return; FD_SET(fd, &readfds); int ret = select(fd + 1, &readfds, NULL, NULL, msec > -1 ? &timeout : NULL); if(ret == 0) timedout(tt); else if(ret > 0) termkey_advisereadable(tk); check_resize(tt); get_keys(tt, tk); } void tickit_term_input_wait_tv(TickitTerm *tt, const struct timeval *timeout) { if(timeout) tickit_term_input_wait_msec(tt, (long)(timeout->tv_sec) + (timeout->tv_usec / 1000)); else tickit_term_input_wait_msec(tt, -1); } void tickit_term_flush(TickitTerm *tt) { if(tt->outbuffer_cur == 0) return; if(tt->outfunc) (*tt->outfunc)(tt, tt->outbuffer, tt->outbuffer_cur, tt->outfunc_user); else if(tt->outfd != -1) { write(tt->outfd, tt->outbuffer, tt->outbuffer_cur); } tt->outbuffer_cur = 0; } static void write_str(TickitTerm *tt, const char *str, size_t len) { if(len == 0) len = strlen(str); if(tt->outbuffer) { while(len > 0) { size_t space = tt->outbuffer_len - tt->outbuffer_cur; if(len < space) space = len; memcpy(tt->outbuffer + tt->outbuffer_cur, str, space); tt->outbuffer_cur += space; str += space; len -= space; if(tt->outbuffer_cur >= tt->outbuffer_len) tickit_term_flush(tt); } } else if(tt->outfunc) { (*tt->outfunc)(tt, str, len, tt->outfunc_user); } else if(tt->outfd != -1) { write(tt->outfd, str, len); } } /* Driver API */ void tickit_termdrv_write_str(TickitTermDriver *ttd, const char *str, size_t len) { write_str(ttd->tt, str, len); } static void write_vstrf(TickitTerm *tt, const char *fmt, va_list args) { /* It's likely the output will fit in, say, 64 bytes */ char buffer[64]; size_t len; { va_list args_for_size; va_copy(args_for_size, args); len = vsnprintf(buffer, sizeof buffer, fmt, args_for_size); va_end(args_for_size); } if(len < sizeof buffer) { write_str(tt, buffer, len); return; } char *morebuffer = get_tmpbuffer(tt, len + 1); vsnprintf(morebuffer, len + 1, fmt, args); write_str(tt, morebuffer, len); } /* Driver API */ void tickit_termdrv_write_strf(TickitTermDriver *ttd, const char *fmt, ...) { va_list args; va_start(args, fmt); write_vstrf(ttd->tt, fmt, args); va_end(args); } void tickit_term_print(TickitTerm *tt, const char *str) { (*tt->driver->vtable->print)(tt->driver, str, strlen(str)); } void tickit_term_printn(TickitTerm *tt, const char *str, size_t len) { (*tt->driver->vtable->print)(tt->driver, str, len); } void tickit_term_printf(TickitTerm *tt, const char *fmt, ...) { va_list args; va_start(args, fmt); tickit_term_vprintf(tt, fmt, args); va_end(args); } void tickit_term_vprintf(TickitTerm *tt, const char *fmt, va_list args) { va_list args2; va_copy(args2, args); size_t len = vsnprintf(NULL, 0, fmt, args); char *buf = get_tmpbuffer(tt, len + 1); vsnprintf(buf, len + 1, fmt, args2); (*tt->driver->vtable->print)(tt->driver, buf, len); va_end(args2); } bool tickit_term_goto(TickitTerm *tt, int line, int col) { return (*tt->driver->vtable->goto_abs)(tt->driver, line, col); } void tickit_term_move(TickitTerm *tt, int downward, int rightward) { (*tt->driver->vtable->move_rel)(tt->driver, downward, rightward); } bool tickit_term_scrollrect(TickitTerm *tt, TickitRect rect, int downward, int rightward) { return (*tt->driver->vtable->scrollrect)(tt->driver, &rect, downward, rightward); } static int convert_colour(int index, int colours) { if(colours >= 16) return xterm256[index].as16; else return xterm256[index].as8; } void tickit_term_chpen(TickitTerm *tt, const TickitPen *pen) { TickitPen *delta = tickit_pen_new(); for(TickitPenAttr attr = 1; attr < TICKIT_N_PEN_ATTRS; attr++) { if(!tickit_pen_has_attr(pen, attr)) continue; if(tickit_pen_has_attr(tt->pen, attr) && tickit_pen_equiv_attr(tt->pen, pen, attr)) continue; int index; if((attr == TICKIT_PEN_FG || attr == TICKIT_PEN_BG) && (index = tickit_pen_get_colour_attr(pen, attr)) >= tt->colors) { index = convert_colour(index, tt->colors); tickit_pen_set_colour_attr(tt->pen, attr, index); tickit_pen_set_colour_attr(delta, attr, index); } else { tickit_pen_copy_attr(tt->pen, pen, attr); tickit_pen_copy_attr(delta, pen, attr); } } (*tt->driver->vtable->chpen)(tt->driver, delta, tt->pen); tickit_pen_unref(delta); } void tickit_term_setpen(TickitTerm *tt, const TickitPen *pen) { TickitPen *delta = tickit_pen_new(); for(TickitPenAttr attr = 1; attr < TICKIT_N_PEN_ATTRS; attr++) { if(tickit_pen_has_attr(tt->pen, attr) && tickit_pen_equiv_attr(tt->pen, pen, attr)) continue; int index; if((attr == TICKIT_PEN_FG || attr == TICKIT_PEN_BG) && (index = tickit_pen_get_colour_attr(pen, attr)) >= tt->colors) { index = convert_colour(index, tt->colors); tickit_pen_set_colour_attr(tt->pen, attr, index); tickit_pen_set_colour_attr(delta, attr, index); } else { tickit_pen_copy_attr(tt->pen, pen, attr); tickit_pen_copy_attr(delta, pen, attr); } } (*tt->driver->vtable->chpen)(tt->driver, delta, tt->pen); tickit_pen_unref(delta); } /* Driver API */ TickitPen *tickit_termdrv_current_pen(TickitTermDriver *ttd) { return ttd->tt->pen; } void tickit_term_clear(TickitTerm *tt) { (*tt->driver->vtable->clear)(tt->driver); } void tickit_term_erasech(TickitTerm *tt, int count, TickitMaybeBool moveend) { (*tt->driver->vtable->erasech)(tt->driver, count, moveend); } bool tickit_term_getctl_int(TickitTerm *tt, TickitTermCtl ctl, int *value) { return (*tt->driver->vtable->getctl_int)(tt->driver, ctl, value); } bool tickit_term_setctl_int(TickitTerm *tt, TickitTermCtl ctl, int value) { return (*tt->driver->vtable->setctl_int)(tt->driver, ctl, value); } bool tickit_term_setctl_str(TickitTerm *tt, TickitTermCtl ctl, const char *value) { return (*tt->driver->vtable->setctl_str)(tt->driver, ctl, value); } void tickit_term_pause(TickitTerm *tt) { if(tt->driver->vtable->pause) (*tt->driver->vtable->pause)(tt->driver); if(tt->termkey) termkey_stop(tt->termkey); } void tickit_term_resume(TickitTerm *tt) { if(tt->termkey) termkey_start(tt->termkey); if(tt->driver->vtable->resume) (*tt->driver->vtable->resume)(tt->driver); } const char *tickit_term_ctlname(TickitTermCtl ctl) { switch(ctl) { case TICKIT_TERMCTL_ALTSCREEN: return "altscreen"; case TICKIT_TERMCTL_CURSORVIS: return "cursorvis"; case TICKIT_TERMCTL_MOUSE: return "mouse"; case TICKIT_TERMCTL_CURSORBLINK: return "cursorblink"; case TICKIT_TERMCTL_CURSORSHAPE: return "cursorshape"; case TICKIT_TERMCTL_ICON_TEXT: return "icon_text"; case TICKIT_TERMCTL_TITLE_TEXT: return "title_text"; case TICKIT_TERMCTL_ICONTITLE_TEXT: return "icontitle_text"; case TICKIT_TERMCTL_KEYPAD_APP: return "keypad_app"; case TICKIT_TERMCTL_COLORS: return "colors"; case TICKIT_N_TERMCTLS: ; } return NULL; } TickitTermCtl tickit_term_lookup_ctl(const char *name) { const char *s; for(TickitTermCtl ctl = 1; ctl < TICKIT_N_TERMCTLS; ctl++) if((s = tickit_term_ctlname(ctl)) && streq(name, s)) return ctl; return -1; } TickitType tickit_term_ctltype(TickitTermCtl ctl) { switch(ctl) { case TICKIT_TERMCTL_ALTSCREEN: case TICKIT_TERMCTL_CURSORVIS: case TICKIT_TERMCTL_CURSORBLINK: case TICKIT_TERMCTL_KEYPAD_APP: return TICKIT_TYPE_BOOL; case TICKIT_TERMCTL_COLORS: case TICKIT_TERMCTL_CURSORSHAPE: case TICKIT_TERMCTL_MOUSE: return TICKIT_TYPE_INT; case TICKIT_TERMCTL_ICON_TEXT: case TICKIT_TERMCTL_ICONTITLE_TEXT: case TICKIT_TERMCTL_TITLE_TEXT: return TICKIT_TYPE_STR; case TICKIT_N_TERMCTLS: ; } return TICKIT_TYPE_NONE; } libtickit-0.3.4/src/termdriver-ti.c0000644000000000000000000003642513613430267015371 0ustar 00000000000000#include "termdriver.h" // This entire driver requires unibilium #ifdef HAVE_UNIBILIUM #include "unibilium.h" #include #include /* I would love if terminfo gave us these extra strings, but it does not. At a * minor risk of non-portability, define them here. */ struct TermInfoExtraStrings { const char *enter_altscreen_mode; const char *exit_altscreen_mode; const char *enter_mouse_mode; const char *exit_mouse_mode; }; static const struct TermInfoExtraStrings extra_strings_default = { // Alternate screen + cursor save mode .enter_altscreen_mode = "\e[?1049h", .exit_altscreen_mode = "\e[?1049l", }; static const struct TermInfoExtraStrings extra_strings_vt200_mouse = { // Alternate screen + cursor save mode .enter_altscreen_mode = "\e[?1049h", .exit_altscreen_mode = "\e[?1049l", // Mouse click/drag reporting // Also speculatively enable SGR protocol .enter_mouse_mode = "\e[?1002h\e[?1006h", .exit_mouse_mode = "\e[?1002l\e[?1006l", }; /* Also, some terminfo databases are incomplete. Lets provide some fallbacks */ struct TermInfoFallback { enum unibi_string cap; const char *value; }; const static struct TermInfoFallback terminfo_fallback[] = { { unibi_erase_chars, "\e[%dX" }, { unibi_parm_dch, "\e[%dP" }, { 0 }, }; struct TIDriver { TickitTermDriver driver; unibi_term *ut; struct { unsigned int altscreen:1; unsigned int cursorvis:1; unsigned int mouse:1; } mode; struct { unsigned int bce:1; int colours; } cap; struct { // Positioning const char *cup; // cursor_address const char *vpa; // row_address == vertical position absolute const char *hpa; // column_address = horizontal position absolute // Moving const char *cuu; const char *cuu1; // Cursor Up const char *cud; const char *cud1; // Cursor Down const char *cuf; const char *cuf1; // Cursor Forward == Right const char *cub; const char *cub1; // Cursor Backward == Left // Editing const char *ich; const char *ich1; // Insert Character const char *dch; const char *dch1; // Delete Character const char *il; const char *il1; // Insert Line const char *dl; const char *dl1; // Delete Line const char *ech; // Erase Character const char *ed2; // Erase Data 2 == Clear screen const char *stbm; // Set Top/Bottom Margins // Formatting const char *sgr; // Select Graphic Rendition const char *sgr0; // Exit Attribute Mode const char *sgr_i0, *sgr_i1; // SGR italic off/on const char *sgr_fg; // SGR foreground colour const char *sgr_bg; // SGR background colour // Mode setting/clearing const char *sm_csr; const char *rm_csr; // Set/reset mode: Cursor visible } str; const struct TermInfoExtraStrings *extra; }; static const char *lookup_ti_string(struct TIDriver *td, const TickitTermProbeArgs *args, enum unibi_string s) { const char *value = unibi_get_str(td->ut, s); for(const struct TermInfoFallback *fb = terminfo_fallback; !value && fb->cap; fb++) if(fb->cap == s) value = fb->value; if(args->ti_hook && args->ti_hook->getstr) value = (*args->ti_hook->getstr)(unibi_name_str(s), value, args->ti_hook->data); return value; } static const char *require_ti_string(struct TIDriver *td, const TickitTermProbeArgs *args, enum unibi_string s) { const char *ret = lookup_ti_string(td, args, s); if(ret) return ret; fprintf(stderr, "Required TI string '%s' is missing\n", unibi_name_str(s)); abort(); } static bool print(TickitTermDriver *ttd, const char *str, size_t len) { tickit_termdrv_write_str(ttd, str, len); return true; } static void run_ti(TickitTermDriver *ttd, const char *str, int n_params, ...) { unibi_var_t params[9]; va_list args; if(!str) { fprintf(stderr, "Abort on attempt to use NULL TI string\n"); abort(); } va_start(args, n_params); for(int i = 0; i < 10 && i < n_params; i++) params[i] = unibi_var_from_num(va_arg(args, int)); char tmp[64]; char *buf = tmp; size_t len = unibi_run(str, params, buf, sizeof(tmp)); if(len > sizeof(tmp)) { buf = tickit_termdrv_get_tmpbuffer(ttd, len); unibi_run(str, params, buf, len); } tickit_termdrv_write_str(ttd, buf, len); } static bool goto_abs(TickitTermDriver *ttd, int line, int col) { struct TIDriver *td = (struct TIDriver*)ttd; if(line != -1 && col != -1) run_ti(ttd, td->str.cup, 2, line, col); else if(line != -1) { if(!td->str.vpa) return false; run_ti(ttd, td->str.vpa, 1, line); } else if(col != -1) { if(col == 0) { tickit_termdrv_write_str(ttd, "\r", 1); return true; } if(td->str.hpa) run_ti(ttd, td->str.hpa, 1, col); else if(td->str.cuf) { // Emulate HPA by CR + CUF tickit_termdrv_write_str(ttd, "\r", 1); run_ti(ttd, td->str.cuf, 1, col); } else return false; } return true; } static bool move_rel(TickitTermDriver *ttd, int downward, int rightward) { struct TIDriver *td = (struct TIDriver*)ttd; if(downward == 1 && td->str.cud1) run_ti(ttd, td->str.cud1, 0); else if(downward == -1 && td->str.cuu1) run_ti(ttd, td->str.cuu1, 0); else if(downward > 0) run_ti(ttd, td->str.cud, 1, downward); else if(downward < 0) run_ti(ttd, td->str.cuu, 1, -downward); if(rightward == 1 && td->str.cuf1) run_ti(ttd, td->str.cuf1, 0); else if(rightward == -1 && td->str.cub1) run_ti(ttd, td->str.cub1, 0); else if(rightward > 0) run_ti(ttd, td->str.cuf, 1, rightward); else if(rightward < 0) run_ti(ttd, td->str.cub, 1, -rightward); return true; } static bool scrollrect(TickitTermDriver *ttd, const TickitRect *rect, int downward, int rightward) { struct TIDriver *td = (struct TIDriver*)ttd; if(!downward && !rightward) return true; int term_lines, term_cols; tickit_term_get_size(ttd->tt, &term_lines, &term_cols); if((tickit_rect_right(rect) == term_cols) && downward == 0) { for(int line = rect->top; line < tickit_rect_bottom(rect); line++) { goto_abs(ttd, line, rect->left); if(rightward == 1 && td->str.dch1) run_ti(ttd, td->str.dch1, 0); else if(rightward == -1 && td->str.ich1) run_ti(ttd, td->str.ich1, 0); else if(rightward > 0) run_ti(ttd, td->str.dch, 1, rightward); else if(rightward < 0) run_ti(ttd, td->str.ich, 1, -rightward); } return true; } if(rect->left == 0 && rect->cols == term_cols && rightward == 0) { run_ti(ttd, td->str.stbm, 2, rect->top, tickit_rect_bottom(rect) - 1); goto_abs(ttd, rect->top, 0); if(downward == 1 && td->str.dl1) run_ti(ttd, td->str.dl1, 0); else if(downward == -1 && td->str.il1) run_ti(ttd, td->str.il1, 0); else if(downward > 0) run_ti(ttd, td->str.dl, 1, downward); else if(downward < 0) run_ti(ttd, td->str.il, 1, -downward); run_ti(ttd, td->str.stbm, 2, 0, term_lines - 1); return true; } return false; } static bool erasech(TickitTermDriver *ttd, int count, TickitMaybeBool moveend) { struct TIDriver *td = (struct TIDriver *)ttd; if(count < 1) return true; /* Even if the terminal can do bce, only use ECH if we're not in * reverse-video mode. Most terminals don't do rv+ECH properly */ if(td->cap.bce && !tickit_pen_get_bool_attr(tickit_termdrv_current_pen(ttd), TICKIT_PEN_REVERSE)) { run_ti(ttd, td->str.ech, 1, count); if(moveend == TICKIT_YES) move_rel(ttd, 0, count); } else { /* TODO: consider tickit_termdrv_write_chrfill(ttd, c, n) */ char *spaces = tickit_termdrv_get_tmpbuffer(ttd, 64); memset(spaces, ' ', 64); while(count > 64) { tickit_termdrv_write_str(ttd, spaces, 64); count -= 64; } tickit_termdrv_write_str(ttd, spaces, count); if(moveend == TICKIT_NO) move_rel(ttd, 0, -count); } return true; } static bool clear(TickitTermDriver *ttd) { struct TIDriver *td = (struct TIDriver *)ttd; run_ti(ttd, td->str.ed2, 0); return true; } static bool chpen(TickitTermDriver *ttd, const TickitPen *delta, const TickitPen *final) { struct TIDriver *td = (struct TIDriver *)ttd; /* TODO: This is all a bit of a mess * Would be nicer to detect if fg/bg colour are changed, and if not, use * the individual enter/exit modes from the delta pen */ run_ti(ttd, td->str.sgr, 9, 0, // standout tickit_pen_get_bool_attr(final, TICKIT_PEN_UNDER), tickit_pen_get_bool_attr(final, TICKIT_PEN_REVERSE), tickit_pen_get_bool_attr(final, TICKIT_PEN_BLINK), 0, // dim tickit_pen_get_bool_attr(final, TICKIT_PEN_BOLD), 0, // invisible 0, // protect 0); // alt charset if(tickit_pen_has_attr(delta, TICKIT_PEN_ITALIC)) { if(td->str.sgr_i1 && tickit_pen_get_bool_attr(delta, TICKIT_PEN_ITALIC)) run_ti(ttd, td->str.sgr_i1, 0); else if(td->str.sgr_i0) run_ti(ttd, td->str.sgr_i0, 0); } int c; if((c = tickit_pen_get_colour_attr(final, TICKIT_PEN_FG)) > -1 && c < td->cap.colours) run_ti(ttd, td->str.sgr_fg, 1, c); if((c = tickit_pen_get_colour_attr(final, TICKIT_PEN_BG)) > -1 && c < td->cap.colours) run_ti(ttd, td->str.sgr_bg, 1, c); return true; } static bool getctl_int(TickitTermDriver *ttd, TickitTermCtl ctl, int *value) { struct TIDriver *td = (struct TIDriver *)ttd; switch(ctl) { case TICKIT_TERMCTL_ALTSCREEN: *value = td->mode.altscreen; return true; case TICKIT_TERMCTL_CURSORVIS: *value = td->mode.cursorvis; return true; case TICKIT_TERMCTL_MOUSE: *value = td->mode.mouse; return true; case TICKIT_TERMCTL_COLORS: *value = td->cap.colours; return true; default: return false; } } static bool setctl_int(TickitTermDriver *ttd, TickitTermCtl ctl, int value) { struct TIDriver *td = (struct TIDriver *)ttd; switch(ctl) { case TICKIT_TERMCTL_ALTSCREEN: if(!td->extra->enter_altscreen_mode) return false; if(!td->mode.altscreen == !value) return true; tickit_termdrv_write_str(ttd, value ? td->extra->enter_altscreen_mode : td->extra->exit_altscreen_mode, 0); td->mode.altscreen = !!value; return true; case TICKIT_TERMCTL_CURSORVIS: if(!td->mode.cursorvis == !value) return true; run_ti(ttd, value ? td->str.sm_csr : td->str.rm_csr, 0); td->mode.cursorvis = !!value; return true; case TICKIT_TERMCTL_MOUSE: if(!td->extra->enter_mouse_mode) return false; if(!td->mode.mouse == !value) return true; tickit_termdrv_write_str(ttd, value ? td->extra->enter_mouse_mode : td->extra->exit_mouse_mode, 0); td->mode.mouse = !!value; return true; default: return false; } } static bool setctl_str(TickitTermDriver *ttd, TickitTermCtl ctl, const char *value) { return false; } static void attach(TickitTermDriver *ttd, TickitTerm *tt) { struct TIDriver *td = (struct TIDriver *)ttd; unibi_term *ut = td->ut; tickit_term_set_size(tt, unibi_get_num(ut, unibi_lines), unibi_get_num(ut, unibi_columns)); } static void start(TickitTermDriver *ttd) { // Nothing needed } static void shutdown(TickitTermDriver *ttd) { struct TIDriver *td = (struct TIDriver *)ttd; if(td->mode.mouse) tickit_termdrv_write_str(ttd, td->extra->exit_mouse_mode, 0); if(!td->mode.cursorvis) run_ti(ttd, td->str.sm_csr, 0); if(td->mode.altscreen) tickit_termdrv_write_str(ttd, td->extra->exit_altscreen_mode, 0); run_ti(ttd, td->str.sgr0, 0); } static void resume(TickitTermDriver *ttd) { struct TIDriver *td = (struct TIDriver *)ttd; if(td->mode.altscreen) tickit_termdrv_write_str(ttd, td->extra->enter_altscreen_mode, 0); if(!td->mode.cursorvis) run_ti(ttd, td->str.rm_csr, 0); if(td->mode.mouse) tickit_termdrv_write_str(ttd, td->extra->enter_mouse_mode, 0); } static void destroy(TickitTermDriver *ttd) { struct TIDriver *td = (struct TIDriver *)ttd; unibi_destroy(td->ut); free(td); } static TickitTermDriverVTable ti_vtable = { .attach = attach, .destroy = destroy, .start = start, .stop = shutdown, .pause = shutdown, .resume = resume, .print = print, .goto_abs = goto_abs, .move_rel = move_rel, .scrollrect = scrollrect, .erasech = erasech, .clear = clear, .chpen = chpen, .getctl_int = getctl_int, .setctl_int = setctl_int, .setctl_str = setctl_str, }; static TickitTermDriver *new(const TickitTermProbeArgs *args) { unibi_term *ut = unibi_from_term(args->termtype); if(!ut) return NULL; struct TIDriver *td = malloc(sizeof(struct TIDriver)); td->driver.vtable = &ti_vtable; td->ut = ut; td->mode.mouse = 0; td->mode.cursorvis = 1; td->mode.altscreen = 0; td->cap.bce = unibi_get_bool(ut, unibi_back_color_erase); td->cap.colours = unibi_get_num(ut, unibi_max_colors); td->str.cup = require_ti_string(td, args, unibi_cursor_address); td->str.vpa = lookup_ti_string (td, args, unibi_row_address); td->str.hpa = lookup_ti_string (td, args, unibi_column_address); td->str.cuu = require_ti_string(td, args, unibi_parm_up_cursor); td->str.cuu1 = lookup_ti_string (td, args, unibi_cursor_up); td->str.cud = require_ti_string(td, args, unibi_parm_down_cursor); td->str.cud1 = lookup_ti_string (td, args, unibi_cursor_down); td->str.cuf = require_ti_string(td, args, unibi_parm_right_cursor); td->str.cuf1 = lookup_ti_string (td, args, unibi_cursor_right); td->str.cub = require_ti_string(td, args, unibi_parm_left_cursor); td->str.cub1 = lookup_ti_string (td, args, unibi_cursor_left); td->str.ich = require_ti_string(td, args, unibi_parm_ich); td->str.ich1 = lookup_ti_string (td, args, unibi_insert_character); td->str.dch = require_ti_string(td, args, unibi_parm_dch); td->str.dch1 = lookup_ti_string (td, args, unibi_delete_character); td->str.il = require_ti_string(td, args, unibi_parm_insert_line); td->str.il1 = lookup_ti_string (td, args, unibi_insert_line); td->str.dl = require_ti_string(td, args, unibi_parm_delete_line); td->str.dl1 = lookup_ti_string (td, args, unibi_delete_line); td->str.ech = require_ti_string(td, args, unibi_erase_chars); td->str.ed2 = require_ti_string(td, args, unibi_clear_screen); td->str.stbm = require_ti_string(td, args, unibi_change_scroll_region); td->str.sgr = require_ti_string(td, args, unibi_set_attributes); td->str.sgr0 = require_ti_string(td, args, unibi_exit_attribute_mode); td->str.sgr_i0 = lookup_ti_string (td, args, unibi_exit_italics_mode); td->str.sgr_i1 = lookup_ti_string (td, args, unibi_enter_italics_mode); td->str.sgr_fg = require_ti_string(td, args, unibi_set_a_foreground); td->str.sgr_bg = require_ti_string(td, args, unibi_set_a_background); td->str.sm_csr = require_ti_string(td, args, unibi_cursor_normal); td->str.rm_csr = require_ti_string(td, args, unibi_cursor_invisible); const char *key_mouse = lookup_ti_string(td, args, unibi_key_mouse); if(key_mouse && strcmp(key_mouse, "\e[M") == 0) td->extra = &extra_strings_vt200_mouse; else td->extra = &extra_strings_default; return (TickitTermDriver*)td; } #else /* not HAVE_UNIBILIUM */ static TickitTermDriver *new(const TickitTermProbeArgs *args) { return NULL; } #endif TickitTermDriverProbe tickit_termdrv_probe_ti = { .new = new, }; libtickit-0.3.4/src/termdriver-xterm.c0000644000000000000000000004310313613430267016103 0ustar 00000000000000#include "termdriver.h" #include #include #include #define strneq(a,b,n) (strncmp(a,b,n)==0) #define CSI_MORE_SUBPARAM 0x80000000 #define CSI_NEXT_SUB(x) ((x) & CSI_MORE_SUBPARAM) #define CSI_PARAM(x) ((x) & ~CSI_MORE_SUBPARAM) struct XTermDriver { TickitTermDriver driver; int dcs_offset; char dcs_buffer[16]; struct { unsigned int altscreen:1; unsigned int cursorvis:1; unsigned int cursorblink:1; unsigned int cursorshape:2; unsigned int mouse:2; unsigned int keypad:1; } mode; struct { unsigned int cursorshape:1; unsigned int slrm:1; unsigned int csi_sub_colon:1; unsigned int rgb8:1; } cap; struct { unsigned int cursorvis:1; unsigned int cursorblink:1; unsigned int cursorshape:2; unsigned int slrm:1; } initialised; }; static bool print(TickitTermDriver *ttd, const char *str, size_t len) { tickit_termdrv_write_str(ttd, str, len); return true; } static bool goto_abs(TickitTermDriver *ttd, int line, int col) { if(line != -1 && col > 0) tickit_termdrv_write_strf(ttd, "\e[%d;%dH", line+1, col+1); else if(line != -1 && col == 0) tickit_termdrv_write_strf(ttd, "\e[%dH", line+1); else if(line != -1) tickit_termdrv_write_strf(ttd, "\e[%dd", line+1); else if(col > 0) tickit_termdrv_write_strf(ttd, "\e[%dG", col+1); else if(col != -1) tickit_termdrv_write_str(ttd, "\e[G", 3); return true; } static bool move_rel(TickitTermDriver *ttd, int downward, int rightward) { if(downward > 1) tickit_termdrv_write_strf(ttd, "\e[%dB", downward); else if(downward == 1) tickit_termdrv_write_str(ttd, "\e[B", 3); else if(downward == -1) tickit_termdrv_write_str(ttd, "\e[A", 3); else if(downward < -1) tickit_termdrv_write_strf(ttd, "\e[%dA", -downward); if(rightward > 1) tickit_termdrv_write_strf(ttd, "\e[%dC", rightward); else if(rightward == 1) tickit_termdrv_write_str(ttd, "\e[C", 3); else if(rightward == -1) tickit_termdrv_write_str(ttd, "\e[D", 3); else if(rightward < -1) tickit_termdrv_write_strf(ttd, "\e[%dD", -rightward); return true; } static bool scrollrect(TickitTermDriver *ttd, const TickitRect *rect, int downward, int rightward) { struct XTermDriver *xd = (struct XTermDriver *)ttd; if(!downward && !rightward) return true; int term_cols; tickit_term_get_size(ttd->tt, NULL, &term_cols); int right = tickit_rect_right(rect); /* Use DECSLRM only for 1 line of insert/delete, because any more and it's * likely better to use the generic system below */ if(((xd->cap.slrm && rect->lines == 1) || (right == term_cols)) && downward == 0) { if(right < term_cols) tickit_termdrv_write_strf(ttd, "\e[;%ds", right); for(int line = rect->top; line < tickit_rect_bottom(rect); line++) { goto_abs(ttd, line, rect->left); if(rightward > 1) tickit_termdrv_write_strf(ttd, "\e[%dP", rightward); /* DCH */ else if(rightward == 1) tickit_termdrv_write_str(ttd, "\e[P", 3); /* DCH1 */ else if(rightward == -1) tickit_termdrv_write_str(ttd, "\e[@", 3); /* ICH1 */ else if(rightward < -1) tickit_termdrv_write_strf(ttd, "\e[%d@", -rightward); /* ICH */ } if(right < term_cols) tickit_termdrv_write_strf(ttd, "\e[s"); return true; } if(xd->cap.slrm || (rect->left == 0 && rect->cols == term_cols && rightward == 0)) { tickit_termdrv_write_strf(ttd, "\e[%d;%dr", rect->top + 1, tickit_rect_bottom(rect)); if(rect->left > 0 || right < term_cols) tickit_termdrv_write_strf(ttd, "\e[%d;%ds", rect->left + 1, right); goto_abs(ttd, rect->top, rect->left); if(downward > 1) tickit_termdrv_write_strf(ttd, "\e[%dM", downward); /* DL */ else if(downward == 1) tickit_termdrv_write_str(ttd, "\e[M", 3); /* DL1 */ else if(downward == -1) tickit_termdrv_write_str(ttd, "\e[L", 3); /* IL1 */ else if(downward < -1) tickit_termdrv_write_strf(ttd, "\e[%dL", -downward); /* IL */ if(rightward > 1) tickit_termdrv_write_strf(ttd, "\e[%d'~", rightward); /* DECDC */ else if(rightward == 1) tickit_termdrv_write_str(ttd, "\e['~", 4); /* DECDC1 */ else if(rightward == -1) tickit_termdrv_write_str(ttd, "\e['}", 4); /* DECIC1 */ if(rightward < -1) tickit_termdrv_write_strf(ttd, "\e[%d'}", -rightward); /* DECIC */ tickit_termdrv_write_str(ttd, "\e[r", 3); if(rect->left > 0 || right < term_cols) tickit_termdrv_write_str(ttd, "\e[s", 3); return true; } return false; } static bool erasech(TickitTermDriver *ttd, int count, TickitMaybeBool moveend) { if(count < 1) return true; /* Only use ECH if we're not in reverse-video mode. xterm doesn't do rv+ECH * properly */ if(!tickit_pen_get_bool_attr(tickit_termdrv_current_pen(ttd), TICKIT_PEN_REVERSE)) { if(count == 1) tickit_termdrv_write_str(ttd, "\e[X", 3); else tickit_termdrv_write_strf(ttd, "\e[%dX", count); if(moveend == TICKIT_YES) move_rel(ttd, 0, count); } else { /* TODO: consider tickit_termdrv_write_chrfill(ttd, c, n) */ char *spaces = tickit_termdrv_get_tmpbuffer(ttd, 64); memset(spaces, ' ', 64); while(count > 64) { tickit_termdrv_write_str(ttd, spaces, 64); count -= 64; } tickit_termdrv_write_str(ttd, spaces, count); if(moveend == TICKIT_NO) move_rel(ttd, 0, -count); } return true; } static bool clear(TickitTermDriver *ttd) { tickit_termdrv_write_strf(ttd, "\e[2J", 4); return true; } static struct SgrOnOff { int on, off; } sgr_onoff[] = { {}, /* none */ { 30, 39 }, /* fg */ { 40, 49 }, /* bg */ { 1, 22 }, /* bold */ { 4, 24 }, /* under */ { 3, 23 }, /* italic */ { 7, 27 }, /* reverse */ { 9, 29 }, /* strike */ { 10, 10 }, /* altfont */ { 5, 25 }, /* blink */ }; static bool chpen(TickitTermDriver *ttd, const TickitPen *delta, const TickitPen *final) { struct XTermDriver *xd = (struct XTermDriver *)ttd; /* There can be at most 16 SGR parameters; 5 from each of 2 colours, and * 6 single attributes */ int params[16]; int pindex = 0; for(TickitPenAttr attr = 1; attr < TICKIT_N_PEN_ATTRS; attr++) { if(!tickit_pen_has_attr(delta, attr)) continue; struct SgrOnOff *onoff = &sgr_onoff[attr]; int val; switch(attr) { case TICKIT_PEN_FG: case TICKIT_PEN_BG: val = tickit_pen_get_colour_attr(delta, attr); if(val < 0) params[pindex++] = onoff->off; else if(xd->cap.rgb8 && tickit_pen_has_colour_attr_rgb8(delta, attr)) { TickitPenRGB8 rgb = tickit_pen_get_colour_attr_rgb8(delta, attr); params[pindex++] = (onoff->on+8) | CSI_MORE_SUBPARAM; params[pindex++] = 2 | CSI_MORE_SUBPARAM; params[pindex++] = rgb.r | CSI_MORE_SUBPARAM; params[pindex++] = rgb.g | CSI_MORE_SUBPARAM; params[pindex++] = rgb.b; } else if(val < 8) params[pindex++] = onoff->on + val; else if(val < 16) params[pindex++] = onoff->on+60 + val-8; else { params[pindex++] = (onoff->on+8) | CSI_MORE_SUBPARAM; params[pindex++] = 5 | CSI_MORE_SUBPARAM; params[pindex++] = val; } break; case TICKIT_PEN_UNDER: val = tickit_pen_get_int_attr(delta, attr); if(!val) params[pindex++] = onoff->off; else if(val == 1) params[pindex++] = onoff->on; else { params[pindex++] = onoff->on | CSI_MORE_SUBPARAM; params[pindex++] = val; } break; case TICKIT_PEN_ALTFONT: val = tickit_pen_get_int_attr(delta, attr); if(val < 0 || val >= 10) params[pindex++] = onoff->off; else params[pindex++] = onoff->on + val; break; case TICKIT_PEN_BOLD: case TICKIT_PEN_ITALIC: case TICKIT_PEN_REVERSE: case TICKIT_PEN_STRIKE: case TICKIT_PEN_BLINK: val = tickit_pen_get_bool_attr(delta, attr); params[pindex++] = val ? onoff->on : onoff->off; break; case TICKIT_N_PEN_ATTRS: break; } } if(pindex == 0) return true; /* If we're going to clear all the attributes then empty SGR is neater */ if(!tickit_pen_is_nondefault(final)) pindex = 0; /* Render params[] into a CSI string */ size_t len = 3; /* ESC [ ... m */ for(int i = 0; i < pindex; i++) len += snprintf(NULL, 0, "%d", CSI_PARAM(params[i])) + 1; if(pindex > 0) len--; /* Last one has no final separator */ char *buffer = tickit_termdrv_get_tmpbuffer(ttd, len + 1); char *s = buffer; s += sprintf(s, "\e["); for(int i = 0; i < pindex-1; i++) s += sprintf(s, "%d%c", CSI_PARAM(params[i]), CSI_NEXT_SUB(params[i]) && xd->cap.csi_sub_colon ? ':' : ';'); if(pindex > 0) s += sprintf(s, "%d", CSI_PARAM(params[pindex-1])); sprintf(s, "m"); tickit_termdrv_write_str(ttd, buffer, len); return true; } static bool getctl_int(TickitTermDriver *ttd, TickitTermCtl ctl, int *value) { struct XTermDriver *xd = (struct XTermDriver *)ttd; switch(ctl) { case TICKIT_TERMCTL_ALTSCREEN: *value = xd->mode.altscreen; return true; case TICKIT_TERMCTL_CURSORVIS: *value = xd->mode.cursorvis; return true; case TICKIT_TERMCTL_CURSORBLINK: *value = xd->mode.cursorblink; return true; case TICKIT_TERMCTL_MOUSE: *value = xd->mode.mouse; return true; case TICKIT_TERMCTL_CURSORSHAPE: *value = xd->mode.cursorshape; return true; case TICKIT_TERMCTL_KEYPAD_APP: *value = xd->mode.keypad; return true; case TICKIT_TERMCTL_COLORS: *value = xd->cap.rgb8 ? (1<<24) : 256; return true; default: return false; } } static int mode_for_mouse(TickitTermMouseMode mode) { switch(mode) { case TICKIT_TERM_MOUSEMODE_CLICK: return 1000; case TICKIT_TERM_MOUSEMODE_DRAG: return 1002; case TICKIT_TERM_MOUSEMODE_MOVE: return 1003; case TICKIT_TERM_MOUSEMODE_OFF: break; } return 0; } static bool setctl_int(TickitTermDriver *ttd, TickitTermCtl ctl, int value) { struct XTermDriver *xd = (struct XTermDriver *)ttd; switch(ctl) { case TICKIT_TERMCTL_ALTSCREEN: if(!xd->mode.altscreen == !value) return true; tickit_termdrv_write_str(ttd, value ? "\e[?1049h" : "\e[?1049l", 0); xd->mode.altscreen = !!value; return true; case TICKIT_TERMCTL_CURSORVIS: if(!xd->mode.cursorvis == !value) return true; tickit_termdrv_write_str(ttd, value ? "\e[?25h" : "\e[?25l", 0); xd->mode.cursorvis = !!value; return true; case TICKIT_TERMCTL_CURSORBLINK: if(xd->initialised.cursorblink && !xd->mode.cursorblink == !value) return true; tickit_termdrv_write_str(ttd, value ? "\e[?12h" : "\e[?12l", 0); xd->mode.cursorblink = !!value; return true; case TICKIT_TERMCTL_MOUSE: if(xd->mode.mouse == value) return true; /* Modes 1000, 1002 and 1003 are mutually exclusive; enabling any one * disables the other two */ if(!value) tickit_termdrv_write_strf(ttd, "\e[?%dl\e[?1006l", mode_for_mouse(xd->mode.mouse)); else tickit_termdrv_write_strf(ttd, "\e[?%dh\e[?1006h", mode_for_mouse(value)); xd->mode.mouse = value; return true; case TICKIT_TERMCTL_CURSORSHAPE: if(xd->initialised.cursorshape && xd->mode.cursorshape == value) return true; if(xd->cap.cursorshape) tickit_termdrv_write_strf(ttd, "\e[%d q", value * 2 + (xd->mode.cursorblink ? -1 : 0)); xd->mode.cursorshape = value; return true; case TICKIT_TERMCTL_KEYPAD_APP: if(!xd->mode.keypad == !value) return true; tickit_termdrv_write_strf(ttd, value ? "\e=" : "\e>"); return true; default: return false; } } static bool setctl_str(TickitTermDriver *ttd, TickitTermCtl ctl, const char *value) { switch(ctl) { case TICKIT_TERMCTL_ICON_TEXT: tickit_termdrv_write_strf(ttd, "\e]1;%s\e\\", value); return true; case TICKIT_TERMCTL_TITLE_TEXT: tickit_termdrv_write_strf(ttd, "\e]2;%s\e\\", value); return true; case TICKIT_TERMCTL_ICONTITLE_TEXT: tickit_termdrv_write_strf(ttd, "\e]0;%s\e\\", value); return true; default: return false; } } static void start(TickitTermDriver *ttd) { // Enable DECSLRM tickit_termdrv_write_strf(ttd, "\e[?69h"); // Find out if DECSLRM is actually supported tickit_termdrv_write_strf(ttd, "\e[?69$p"); // Also query the current cursor visibility, blink status, and shape tickit_termdrv_write_strf(ttd, "\e[?25$p\e[?12$p\eP$q q\e\\"); // Try to work out whether the terminal supports 24bit colours (RGB8) and // whether it understands : to separate sub-params tickit_termdrv_write_strf(ttd, "\e[38;5;255m\e[38:2:0:1:2m\eP$qm\e\\\e[m"); /* Some terminals (e.g. xfce4-terminal) don't understand DECRQM and print * the raw bytes directly as output, while still claiming to be TERM=xterm * It doens't hurt at this point to clear the current line just in case. */ tickit_termdrv_write_strf(ttd, "\e[G\e[K"); tickit_term_flush(ttd->tt); } static bool started(TickitTermDriver *ttd) { struct XTermDriver *xd = (struct XTermDriver *)ttd; return xd->initialised.cursorvis && xd->initialised.cursorblink && xd->initialised.cursorshape && xd->initialised.slrm; } static void gotkey_modereport(struct XTermDriver *xd, int initial, int mode, int value) { if(initial == '?') // DEC mode switch(mode) { case 12: // Cursor blink if(value == 1) xd->mode.cursorblink = 1; xd->initialised.cursorblink = 1; break; case 25: // DECTCEM == Cursor visibility if(value == 1) xd->mode.cursorvis = 1; xd->initialised.cursorvis = 1; break; case 69: // DECVSSM if(value == 1 || value == 2) xd->cap.slrm = 1; xd->initialised.slrm = 1; break; } } static void gotkey_decrqss(struct XTermDriver *xd, const char *args, size_t arglen) { if(strneq(args + arglen - 2, " q", 2)) { // DECSCUSR int value; if(sscanf(args, "%d", &value)) { // value==1 or 2 => shape == 1, 3 or 4 => 2, etc.. int shape = (value+1) / 2; xd->mode.cursorshape = shape; xd->cap.cursorshape = 1; } xd->initialised.cursorshape = 1; } else if(strneq(args + arglen - 1, "m", 1)) { // SGR // skip the initial number, find the first separator while(arglen && args[0] >= '0' && args[0] <= '9') args++, arglen--; if(!arglen) return; if(args[0] == ':') xd->cap.csi_sub_colon = 1; args++, arglen--; // If the palette index is 2 then the terminal understands rgb8 int value; if(sscanf(args, "%d", &value) && value == 2) xd->cap.rgb8 = 1; } } static int gotkey(TickitTermDriver *ttd, TermKey *tk, const TermKeyKey *key) { struct XTermDriver *xd = (struct XTermDriver *)ttd; if(key->type == TERMKEY_TYPE_MODEREPORT) { int initial, mode, value; termkey_interpret_modereport(tk, key, &initial, &mode, &value); gotkey_modereport(xd, initial, mode, value); return 1; } else if(key->type == TERMKEY_TYPE_DCS) { const char *dcs; if(termkey_interpret_string(tk, key, &dcs) != TERMKEY_RES_KEY) return 0; if(strneq(dcs, "1$r", 3)) { // Successful DECRQSS gotkey_decrqss(xd, dcs + 3, strlen(dcs + 3)); } // Just eat all the DCSes return 1; } return 0; } static void teardown(TickitTermDriver *ttd) { struct XTermDriver *xd = (struct XTermDriver *)ttd; if(xd->mode.mouse) tickit_termdrv_write_strf(ttd, "\e[?%dl\e[?1006l", mode_for_mouse(xd->mode.mouse)); if(!xd->mode.cursorvis) tickit_termdrv_write_str(ttd, "\e[?25h", 0); if(xd->mode.altscreen) tickit_termdrv_write_str(ttd, "\e[?1049l", 0); if(xd->mode.keypad) tickit_termdrv_write_strf(ttd, "\e>"); // Reset pen tickit_termdrv_write_str(ttd, "\e[m", 3); } static void resume(TickitTermDriver *ttd) { struct XTermDriver *xd = (struct XTermDriver *)ttd; if(xd->mode.keypad) tickit_termdrv_write_strf(ttd, "\e="); if(xd->mode.altscreen) tickit_termdrv_write_str(ttd, "\e[?1049h", 0); if(!xd->mode.cursorvis) tickit_termdrv_write_str(ttd, "\e[?25l", 0); if(xd->mode.mouse) tickit_termdrv_write_strf(ttd, "\e[?%dh\e[?1006h", mode_for_mouse(xd->mode.mouse)); } static void destroy(TickitTermDriver *ttd) { struct XTermDriver *xd = (struct XTermDriver *)ttd; free(xd); } static TickitTermDriverVTable xterm_vtable = { .destroy = destroy, .start = start, .started = started, .stop = teardown, .pause = teardown, .resume = resume, .print = print, .goto_abs = goto_abs, .move_rel = move_rel, .scrollrect = scrollrect, .erasech = erasech, .clear = clear, .chpen = chpen, .getctl_int = getctl_int, .setctl_int = setctl_int, .setctl_str = setctl_str, .gotkey = gotkey, }; static TickitTermDriver *new(const TickitTermProbeArgs *args) { const char *termtype = args->termtype; if(strncmp(termtype, "xterm", 5) != 0) return NULL; switch(termtype[5]) { case 0: case '-': break; default: return NULL; } struct XTermDriver *xd = malloc(sizeof(struct XTermDriver)); xd->driver.vtable = &xterm_vtable; xd->dcs_offset = -1; memset(&xd->mode, 0, sizeof xd->mode); xd->mode.cursorvis = 1; memset(&xd->cap, 0, sizeof xd->cap); memset(&xd->initialised, 0, sizeof xd->initialised); return (TickitTermDriver*)xd; } TickitTermDriverProbe tickit_termdrv_probe_xterm = { .new = new, }; libtickit-0.3.4/src/termdriver.h0000644000000000000000000000056513613430267014760 0ustar 00000000000000#include "tickit.h" #include "tickit-termdrv.h" typedef struct { const char *termtype; const struct TickitTerminfoHook *ti_hook; } TickitTermProbeArgs; typedef struct { TickitTermDriver *(*new)(const TickitTermProbeArgs *args); } TickitTermDriverProbe; extern TickitTermDriverProbe tickit_termdrv_probe_xterm; extern TickitTermDriverProbe tickit_termdrv_probe_ti; libtickit-0.3.4/src/tickit.c0000644000000000000000000003171313613430267014056 0ustar 00000000000000#include "tickit.h" #include "tickit-evloop.h" #include #include #include #include #include #define streq(a,b) (!strcmp(a,b)) /* INTERNAL */ TickitWindow* tickit_window_new_root2(Tickit *t, TickitTerm *term); struct TickitWatch { TickitWatch *next; Tickit *t; // uncounted enum { WATCH_NONE, WATCH_IO, WATCH_TIMER, WATCH_LATER, } type; TickitBindFlags flags; TickitCallbackFn *fn; void *user; union { void *ptr; int i; } evdata; union { struct { int fd; } io; struct { struct timeval at; } timer; }; }; extern TickitEventHooks tickit_evloop_default; struct Tickit { int refcount; TickitTerm *term; TickitWindow *rootwin; TickitWatch *iowatches, *timers, *laters; TickitEventHooks *evhooks; void *evdata; struct TickitTerminfoHook ti_hook; unsigned int done_setup : 1, use_altscreen : 1; }; static int on_term_timeout(Tickit *t, TickitEventFlags flags, void *info, void *user); static int on_term_timeout(Tickit *t, TickitEventFlags flags, void *info, void *user) { int timeout = tickit_term_input_check_timeout_msec(t->term); if(timeout > -1) tickit_watch_timer_after_msec(t, timeout, 0, on_term_timeout, NULL); return 0; } static int on_term_readable(Tickit *t, TickitEventFlags flags, void *info, void *user) { tickit_term_input_readable(t->term); on_term_timeout(t, TICKIT_EV_FIRE, NULL, NULL); return 0; } static void setterm(Tickit *t, TickitTerm *tt); static void setterm(Tickit *t, TickitTerm *tt) { t->term = tt; /* take ownership */ tickit_watch_io_read(t, tickit_term_get_input_fd(tt), 0, on_term_readable, NULL); } static void setupterm(Tickit *t) { TickitTerm *tt = tickit_get_term(t); tickit_term_await_started_msec(tt, 50); if(t->use_altscreen) tickit_term_setctl_int(tt, TICKIT_TERMCTL_ALTSCREEN, 1); tickit_term_setctl_int(tt, TICKIT_TERMCTL_CURSORVIS, 0); tickit_term_setctl_int(tt, TICKIT_TERMCTL_MOUSE, TICKIT_TERM_MOUSEMODE_DRAG); tickit_term_setctl_int(tt, TICKIT_TERMCTL_KEYPAD_APP, 1); tickit_term_clear(tt); tickit_term_flush(tt); t->done_setup = true; } static void teardownterm(Tickit *t) { t->done_setup = false; } Tickit *tickit_new_with_evloop(TickitTerm *tt, TickitEventHooks *evhooks, void *initdata) { Tickit *t = malloc(sizeof(Tickit)); if(!t) return NULL; t->refcount = 1; t->term = NULL; t->rootwin = NULL; t->evhooks = evhooks; t->evdata = (*t->evhooks->init)(t, initdata); if(!t->evdata) goto abort; t->iowatches = NULL; t->timers = NULL; t->laters = NULL; t->ti_hook.getstr = NULL; t->done_setup = false; t->use_altscreen = true; if(tt) setterm(t, tt); return t; abort: free(t); return NULL; } Tickit *tickit_new_for_term(TickitTerm *tt) { return tickit_new_with_evloop(tt, &tickit_evloop_default, NULL); } Tickit *tickit_new_stdio(void) { return tickit_new_for_term(NULL); } static void destroy_watchlist(Tickit *t, TickitWatch *watches, void (*cancelfunc)(void *data, TickitWatch *watch)) { TickitWatch *this, *next; for(this = watches; this; this = next) { next = this->next; if(this->flags & (TICKIT_BIND_UNBIND|TICKIT_BIND_DESTROY)) (*this->fn)(this->t, TICKIT_EV_UNBIND|TICKIT_EV_DESTROY, NULL, this->user); if(cancelfunc) (*cancelfunc)(t->evdata, this); free(this); } } static void tickit_destroy(Tickit *t) { if(t->done_setup) teardownterm(t); if(t->rootwin) tickit_window_unref(t->rootwin); if(t->term) tickit_term_unref(t->term); if(t->iowatches) destroy_watchlist(t, t->iowatches, t->evhooks->cancel_io); if(t->timers) destroy_watchlist(t, t->timers, t->evhooks->cancel_timer); if(t->laters) destroy_watchlist(t, t->laters, t->evhooks->cancel_later); (*t->evhooks->destroy)(t->evdata); free(t); } Tickit *tickit_ref(Tickit *t) { t->refcount++; return t; } void tickit_unref(Tickit *t) { t->refcount--; if(!t->refcount) tickit_destroy(t); } TickitTerm *tickit_get_term(Tickit *t) { if(!t->term) { /* Don't use tickit_term_open_stdio() because that observes SIGWINCH */ struct TickitTermBuilder builder = { 0 }; if(t->ti_hook.getstr) builder.ti_hook = &t->ti_hook; TickitTerm *tt = tickit_term_build(&builder); if(!tt) return NULL; tickit_term_set_input_fd(tt, STDIN_FILENO); tickit_term_set_output_fd(tt, STDOUT_FILENO); tickit_term_set_output_buffer(tt, 4096); setterm(t, tt); } return t->term; } TickitWindow *tickit_get_rootwin(Tickit *t) { if(!t->rootwin) { TickitTerm *tt = tickit_get_term(t); if(!tt) return NULL; t->rootwin = tickit_window_new_root2(t, tt); } return t->rootwin; } bool tickit_getctl_int(Tickit *t, TickitCtl ctl, int *value) { switch(ctl) { case TICKIT_CTL_USE_ALTSCREEN: *value = t->use_altscreen; return true; case TICKIT_N_CTLS: ; } return false; } bool tickit_setctl_int(Tickit *t, TickitCtl ctl, int value) { switch(ctl) { case TICKIT_CTL_USE_ALTSCREEN: t->use_altscreen = value; return true; case TICKIT_N_CTLS: ; } return false; } void tickit_hook_terminfo(Tickit *t, const char *(*getstr)(const char *name, const char *value, void *data), void *data) { t->ti_hook.getstr = getstr; t->ti_hook.data = data; } // TODO: copy the entire SIGWINCH-like structure from term.c // For now we only handle atmost-one running Tickit instance static Tickit *running_tickit; static void sigint(int sig) { if(running_tickit) tickit_stop(running_tickit); } void tickit_tick(Tickit *t, TickitRunFlags flags) { if(!t->done_setup && !(flags & TICKIT_RUN_NOSETUP)) setupterm(t); (*t->evhooks->run)(t->evdata, TICKIT_RUN_ONCE | flags); } void tickit_run(Tickit *t) { running_tickit = t; signal(SIGINT, sigint); if(!t->done_setup) setupterm(t); (*t->evhooks->run)(t->evdata, TICKIT_RUN_DEFAULT); running_tickit = NULL; } void tickit_stop(Tickit *t) { return (*t->evhooks->stop)(t->evdata); } void *tickit_watch_io_read(Tickit *t, int fd, TickitBindFlags flags, TickitCallbackFn *fn, void *user) { TickitWatch *watch = malloc(sizeof(TickitWatch)); if(!watch) return NULL; watch->next = NULL; watch->t = t; watch->type = WATCH_IO; watch->flags = flags & (TICKIT_BIND_UNBIND|TICKIT_BIND_UNBIND); watch->fn = fn; watch->user = user; watch->io.fd = fd; if(!(*t->evhooks->io_read)(t->evdata, fd, flags, watch)) goto fail; TickitWatch **prevp = &t->iowatches; while(*prevp) prevp = &(*prevp)->next; watch->next = *prevp; *prevp = watch; return watch; fail: free(watch); return NULL; } void *tickit_watch_timer_at_tv(Tickit *t, const struct timeval *at, TickitBindFlags flags, TickitCallbackFn *fn, void *user) { TickitWatch *watch = malloc(sizeof(TickitWatch)); if(!watch) return NULL; watch->next = NULL; watch->t = t; watch->type = WATCH_TIMER; watch->flags = flags & (TICKIT_BIND_UNBIND|TICKIT_BIND_DESTROY); watch->fn = fn; watch->user = user; watch->timer.at = *at; if(t->evhooks->timer) if(!(*t->evhooks->timer)(t->evdata, at, flags, watch)) goto fail; TickitWatch **prevp = &t->timers; /* Try to insert in-order at matching timestamp */ while(*prevp && !timercmp(&(*prevp)->timer.at, at, >)) prevp = &(*prevp)->next; watch->next = *prevp; *prevp = watch; return watch; fail: free(watch); return NULL; } void *tickit_watch_timer_at_epoch(Tickit *t, time_t at, TickitBindFlags flags, TickitCallbackFn *func, void *user) { return tickit_watch_timer_at_tv(t, &(struct timeval){ .tv_sec = at, .tv_usec = 0, }, flags, func, user); } void *tickit_watch_timer_after_tv(Tickit *t, const struct timeval *after, TickitBindFlags flags, TickitCallbackFn *fn, void *user) { struct timeval at; gettimeofday(&at, NULL); /* at + after ==> at */ timeradd(&at, after, &at); return tickit_watch_timer_at_tv(t, &at, flags, fn, user); } void *tickit_watch_timer_after_msec(Tickit *t, int msec, TickitBindFlags flags, TickitCallbackFn *fn, void *user) { return tickit_watch_timer_after_tv(t, &(struct timeval){ .tv_sec = msec / 1000, .tv_usec = (msec % 1000) * 1000, }, flags, fn, user); } void *tickit_watch_later(Tickit *t, TickitBindFlags flags, TickitCallbackFn *fn, void *user) { TickitWatch *watch = malloc(sizeof(TickitWatch)); if(!watch) return NULL; watch->next = NULL; watch->t = t; watch->type = WATCH_LATER; watch->flags = flags & (TICKIT_BIND_UNBIND|TICKIT_BIND_DESTROY); watch->fn = fn; watch->user = user; if(t->evhooks->later) if(!(*t->evhooks->later)(t->evdata, flags, watch)) goto fail; TickitWatch **prevp = &t->laters; while(*prevp) prevp = &(*prevp)->next; watch->next = *prevp; *prevp = watch; return watch; fail: free(watch); return NULL; } void tickit_watch_cancel(Tickit *t, void *_watch) { TickitWatch *watch = _watch; TickitWatch **thisp; switch(watch->type) { case WATCH_IO: thisp = &t->iowatches; break; case WATCH_TIMER: thisp = &t->timers; break; case WATCH_LATER: thisp = &t->laters; break; default: return; } while(*thisp) { TickitWatch *this = *thisp; if(this == watch) { *thisp = this->next; if(this->flags & TICKIT_BIND_UNBIND) (*this->fn)(t, TICKIT_EV_UNBIND, NULL, this->user); switch(this->type) { case WATCH_IO: (*t->evhooks->cancel_io)(t->evdata, this); break; case WATCH_TIMER: if(t->evhooks->cancel_timer) (*t->evhooks->cancel_timer)(t->evdata, this); break; case WATCH_LATER: if(t->evhooks->cancel_later) (*t->evhooks->cancel_later)(t->evdata, this); break; default: ; } free(this); } if(!thisp || !*thisp) break; thisp = &(*thisp)->next; } } int tickit_evloop_next_timer_msec(Tickit *t) { if(t->laters) return 0; if(!t->timers) return -1; struct timeval now, delay; gettimeofday(&now, NULL); /* timers->timer.at - now ==> delay */ timersub(&t->timers->timer.at, &now, &delay); int msec = (delay.tv_sec * 1000) + (delay.tv_usec / 1000); if(msec < 0) msec = 0; return msec; } void tickit_evloop_invoke_timers(Tickit *t) { /* detach the later queue before running any events */ TickitWatch *later = t->laters; t->laters = NULL; if(t->timers) { struct timeval now; gettimeofday(&now, NULL); /* timer queue is stored ordered, so we can just eat a prefix * of it */ TickitWatch *this = t->timers; while(this) { if(timercmp(&this->timer.at, &now, >)) break; /* TODO: consider what info might point at */ (*this->fn)(this->t, TICKIT_EV_FIRE|TICKIT_EV_UNBIND, NULL, this->user); TickitWatch *next = this->next; free(this); this = next; } t->timers = this; } while(later) { (*later->fn)(later->t, TICKIT_EV_FIRE|TICKIT_EV_UNBIND, NULL, later->user); TickitWatch *next = later->next; free(later); later = next; } } void *tickit_evloop_get_watch_data(TickitWatch *watch) { return watch->evdata.ptr; } void tickit_evloop_set_watch_data(TickitWatch *watch, void *data) { watch->evdata.ptr = data; } int tickit_evloop_get_watch_data_int(TickitWatch *watch) { return watch->evdata.i; } void tickit_evloop_set_watch_data_int(TickitWatch *watch, int data) { watch->evdata.i = data; } void tickit_evloop_invoke_watch(TickitWatch *watch, TickitEventFlags flags) { (*watch->fn)(watch->t, flags, NULL, watch->user); /* Remove oneshot watches from the list */ TickitWatch **prevp; switch(watch->type) { case WATCH_NONE: case WATCH_IO: return; case WATCH_TIMER: prevp = &watch->t->timers; break; case WATCH_LATER: prevp = &watch->t->laters; break; } while(*prevp) { if(*prevp == watch) { *prevp = watch->next; watch->next = NULL; watch->type = WATCH_NONE; free(watch); return; } prevp = &(*prevp)->next; } } void tickit_evloop_sigwinch(Tickit *t) { if(!t->term) return; tickit_term_refresh_size(t->term); } const char *tickit_ctlname(TickitCtl ctl) { switch(ctl) { case TICKIT_CTL_USE_ALTSCREEN: return "use-altscreen"; case TICKIT_N_CTLS: ; } return NULL; } TickitCtl tickit_lookup_ctl(const char *name) { const char *s; for(TickitCtl ctl = 1; ctl < TICKIT_N_CTLS; ctl++) if((s = tickit_ctlname(ctl)) && streq(name, s)) return ctl; return -1; } TickitType tickit_ctltype(TickitCtl ctl) { switch(ctl) { case TICKIT_CTL_USE_ALTSCREEN: return TICKIT_TYPE_BOOL; case TICKIT_N_CTLS: ; } return TICKIT_TYPE_NONE; } int tickit_version_major(void) { return TICKIT_VERSION_MAJOR; } int tickit_version_minor(void) { return TICKIT_VERSION_MINOR; } int tickit_version_patch(void) { return TICKIT_VERSION_PATCH; } libtickit-0.3.4/src/unicode.h0000644000000000000000000002244413613430267014223 0ustar 00000000000000// vim:ft=c // ### The following from http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c // With modifications: // made functions static // moved 'combining' table to file scope, so other functions can see it // ################################################################### /* * This is an implementation of wcwidth() and wcswidth() (defined in * IEEE Std 1002.1-2001) for Unicode. * * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html * * In fixed-width output devices, Latin characters all occupy a single * "cell" position of equal width, whereas ideographic CJK characters * occupy two such cells. Interoperability between terminal-line * applications and (teletype-style) character terminals using the * UTF-8 encoding requires agreement on which character should advance * the cursor by how many cell positions. No established formal * standards exist at present on which Unicode character shall occupy * how many cell positions on character terminals. These routines are * a first attempt of defining such behavior based on simple rules * applied to data provided by the Unicode Consortium. * * For some graphical characters, the Unicode standard explicitly * defines a character-cell width via the definition of the East Asian * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes. * In all these cases, there is no ambiguity about which width a * terminal shall use. For characters in the East Asian Ambiguous (A) * class, the width choice depends purely on a preference of backward * compatibility with either historic CJK or Western practice. * Choosing single-width for these characters is easy to justify as * the appropriate long-term solution, as the CJK practice of * displaying these characters as double-width comes from historic * implementation simplicity (8-bit encoded characters were displayed * single-width and 16-bit ones double-width, even for Greek, * Cyrillic, etc.) and not any typographic considerations. * * Much less clear is the choice of width for the Not East Asian * (Neutral) class. Existing practice does not dictate a width for any * of these characters. It would nevertheless make sense * typographically to allocate two character cells to characters such * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be * represented adequately with a single-width glyph. The following * routines at present merely assign a single-cell width to all * neutral characters, in the interest of simplicity. This is not * entirely satisfactory and should be reconsidered before * establishing a formal standard in this area. At the moment, the * decision which Not East Asian (Neutral) characters should be * represented by double-width glyphs cannot yet be answered by * applying a simple rule from the Unicode database content. Setting * up a proper standard for the behavior of UTF-8 character terminals * will require a careful analysis not only of each Unicode character, * but also of each presentation form, something the author of these * routines has avoided to do so far. * * http://www.unicode.org/unicode/reports/tr11/ * * Markus Kuhn -- 2007-05-26 (Unicode 5.0) * * Permission to use, copy, modify, and distribute this software * for any purpose and without fee is hereby granted. The author * disclaims all warranties with regard to this software. * * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c */ #include struct interval { int first; int last; }; /* sorted list of non-overlapping intervals of non-spacing characters */ /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ static const struct interval combining[] = { { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, { 0xE0100, 0xE01EF } }; /* auxiliary function for binary search in interval table */ static int bisearch(wchar_t ucs, const struct interval *table, int max) { int min = 0; int mid; if (ucs < table[0].first || ucs > table[max].last) return 0; while (max >= min) { mid = (min + max) / 2; if (ucs > table[mid].last) min = mid + 1; else if (ucs < table[mid].first) max = mid - 1; else return 1; } return 0; } /* The following two functions define the column width of an ISO 10646 * character as follows: * * - The null character (U+0000) has a column width of 0. * * - Other C0/C1 control characters and DEL will lead to a return * value of -1. * * - Non-spacing and enclosing combining characters (general * category code Mn or Me in the Unicode database) have a * column width of 0. * * - SOFT HYPHEN (U+00AD) has a column width of 1. * * - Other format characters (general category code Cf in the Unicode * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. * * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) * have a column width of 0. * * - Spacing characters in the East Asian Wide (W) or East Asian * Full-width (F) category as defined in Unicode Technical * Report #11 have a column width of 2. * * - All remaining characters (including all printable * ISO 8859-1 and WGL4 characters, Unicode control characters, * etc.) have a column width of 1. * * This implementation assumes that wchar_t characters are encoded * in ISO 10646. */ static int mk_wcwidth(wchar_t ucs) { /* test for 8-bit control characters */ if (ucs == 0) return 0; if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) return -1; /* binary search in table of non-spacing characters */ if (bisearch(ucs, combining, sizeof(combining) / sizeof(struct interval) - 1)) return 0; /* if we arrive here, ucs is not a combining or C0/C1 control character */ return 1 + (ucs >= 0x1100 && (ucs <= 0x115f || /* Hangul Jamo init. consonants */ ucs == 0x2329 || ucs == 0x232a || (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) || /* CJK ... Yi */ (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ (ucs >= 0xffe0 && ucs <= 0xffe6) || (ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd))); } libtickit-0.3.4/src/utf8.c0000644000000000000000000001004213613430267013445 0ustar 00000000000000#include "tickit.h" #include #include "unicode.h" static int next_utf8(const char *str, size_t len, uint32_t *cp) { unsigned char b0 = (str++)[0]; int nbytes; if(!len) return -1; if(!b0) return -1; else if(b0 < 0x80) { // ASCII *cp = b0; return 1; } else if(b0 < 0xc0) // C1 or continuation return -1; else if(b0 < 0xe0) { nbytes = 2; *cp = b0 & 0x1f; } else if(b0 < 0xf0) { nbytes = 3; *cp = b0 & 0x0f; } else if(b0 < 0xf8) { nbytes = 4; *cp = b0 & 0x07; } else return -1; if(len < nbytes) return -1; for(int i = 1; i < nbytes; i++) { b0 = (str++)[0]; if(!b0) return -1; *cp <<= 6; *cp |= b0 & 0x3f; } return nbytes; } int tickit_utf8_seqlen(long codepoint) { if(codepoint < 0x0000080) return 1; if(codepoint < 0x0000800) return 2; if(codepoint < 0x0010000) return 3; if(codepoint < 0x0200000) return 4; if(codepoint < 0x4000000) return 5; return 6; } size_t tickit_utf8_put(char *str, size_t len, long codepoint) { int nbytes = tickit_utf8_seqlen(codepoint); if(!str) return nbytes; if(len < nbytes) return -1; // This is easier done backwards int b = nbytes; while(b > 1) { b--; str[b] = 0x80 | (codepoint & 0x3f); codepoint >>= 6; } switch(nbytes) { case 1: str[0] = (codepoint & 0x7f); break; case 2: str[0] = 0xc0 | (codepoint & 0x1f); break; case 3: str[0] = 0xe0 | (codepoint & 0x0f); break; case 4: str[0] = 0xf0 | (codepoint & 0x07); break; case 5: str[0] = 0xf8 | (codepoint & 0x03); break; case 6: str[0] = 0xfc | (codepoint & 0x01); break; } return nbytes; } size_t tickit_utf8_count(const char *str, TickitStringPos *pos, const TickitStringPos *limit) { tickit_stringpos_zero(pos); return tickit_utf8_ncountmore(str, (size_t)-1, pos, limit); } size_t tickit_utf8_countmore(const char *str, TickitStringPos *pos, const TickitStringPos *limit) { return tickit_utf8_ncountmore(str, (size_t)-1, pos, limit); } size_t tickit_utf8_ncount(const char *str, size_t len, TickitStringPos *pos, const TickitStringPos *limit) { tickit_stringpos_zero(pos); return tickit_utf8_ncountmore(str, len, pos, limit); } size_t tickit_utf8_ncountmore(const char *str, size_t len, TickitStringPos *pos, const TickitStringPos *limit) { TickitStringPos here = *pos; size_t start_bytes = pos->bytes; str += pos->bytes; if(len != (size_t)-1) len -= pos->bytes; while(len != 0 && *str) { uint32_t cp; int bytes = next_utf8(str, len, &cp); if(bytes == -1) return -1; /* Abort on C0 or C1 controls */ if(cp < 0x20 || (cp >= 0x80 && cp < 0xa0)) return -1; int width = mk_wcwidth(cp); if(width == -1) return -1; int is_grapheme = (width > 0) ? 1 : 0; if(is_grapheme) // Commit on the previous grapheme *pos = here; if(limit && limit->bytes != (size_t)-1 && here.bytes + bytes > limit->bytes) break; if(limit && limit->codepoints != -1 && here.codepoints + 1 > limit->codepoints) break; if(limit && limit->graphemes != -1 && here.graphemes + is_grapheme > limit->graphemes) break; if(limit && limit->columns != -1 && here.columns + width > limit->columns) break; str += bytes; if(len != (size_t)-1) len -= bytes; here.bytes += bytes; here.codepoints += 1; here.graphemes += is_grapheme; here.columns += width; } if(len == 0 || *str == 0) // Commit on the final grapheme *pos = here; return pos->bytes - start_bytes; } int tickit_utf8_mbswidth(const char *str) { TickitStringPos pos; tickit_utf8_count(str, &pos, NULL); return pos.columns; } int tickit_utf8_byte2col(const char *str, size_t byte) { TickitStringPos limit = INIT_TICKIT_STRINGPOS_LIMIT_BYTES(byte); TickitStringPos pos; tickit_utf8_count(str, &pos, &limit); return pos.columns; } size_t tickit_utf8_col2byte(const char *str, int col) { TickitStringPos limit = INIT_TICKIT_STRINGPOS_LIMIT_COLUMNS(col); TickitStringPos pos; tickit_utf8_count(str, &pos, &limit); return pos.bytes; } libtickit-0.3.4/src/window.c0000644000000000000000000010755113613430267014102 0ustar 00000000000000#include "tickit.h" #include "bindings.h" #include #include #define streq(a,b) (!strcmp(a,b)) #define ROOT_AS_WINDOW(root) ((TickitWindow*)root) #define WINDOW_AS_ROOT(win) ((TickitRootWindow*)win) #define DEBUG_LOGF if(tickit_debug_enabled) tickit_debug_logf typedef enum { TICKIT_HIERARCHY_INSERT_FIRST, TICKIT_HIERARCHY_INSERT_LAST, TICKIT_HIERARCHY_REMOVE, TICKIT_HIERARCHY_RAISE, TICKIT_HIERARCHY_RAISE_FRONT, TICKIT_HIERARCHY_LOWER, TICKIT_HIERARCHY_LOWER_BACK } HierarchyChangeType; struct TickitWindow { TickitWindow *parent; TickitWindow *first_child; TickitWindow *next; TickitWindow *focused_child; TickitPen *pen; TickitRect rect; struct { int line; int col; TickitCursorShape shape; unsigned int visible : 1; int blink : 2; /* -1, 0, 1 */ } cursor; unsigned int is_root : 1; unsigned int is_visible : 1; unsigned int is_focused : 1; unsigned int is_closed : 1; unsigned int steal_input : 1; unsigned int focus_child_notify : 1; int refcount; struct TickitBindings bindings; }; #define WINDOW_PRINTF_FMT "[%dx%d abs@%d,%d]" #define WINDOW_PRINTF_ARGS(w) (w)->rect.cols, (w)->rect.lines, tickit_window_get_abs_geometry(w).left, tickit_window_get_abs_geometry(w).top #define RECT_PRINTF_FMT "[(%d,%d)..(%d,%d)]" #define RECT_PRINTF_ARGS(r) (r).left, (r).top, tickit_rect_right(&(r)), tickit_rect_bottom(&(r)) DEFINE_BINDINGS_FUNCS(window,TickitWindow,TickitWindowEventFn) typedef struct HierarchyChange HierarchyChange; struct HierarchyChange { HierarchyChangeType change; TickitWindow *parent; TickitWindow *win; HierarchyChange *next; }; typedef struct TickitRootWindow TickitRootWindow; struct TickitRootWindow { TickitWindow win; TickitTerm *term; TickitRectSet *damage; HierarchyChange *hierarchy_changes; bool needs_expose; bool needs_restore; bool needs_later_processing; Tickit *tickit; /* uncounted */ int event_ids[3]; // Drag/drop context handling bool mouse_dragging; int mouse_last_button; int mouse_last_line; int mouse_last_col; TickitWindow *drag_source_window; }; static void _request_restore(TickitRootWindow *root); static void _request_later_processing(TickitRootWindow *root); static void _request_hierarchy_change(HierarchyChangeType, TickitWindow *); static void _do_hierarchy_change(HierarchyChangeType change, TickitWindow *parent, TickitWindow *win); static void _purge_hierarchy_changes(TickitWindow *win); static int _handle_key(TickitWindow *win, TickitKeyEventInfo *args); static TickitWindow *_handle_mouse(TickitWindow *win, TickitMouseEventInfo *args); static int on_term_resize(TickitTerm *term, TickitEventFlags flags, void *_info, void *user) { TickitRootWindow *root = user; TickitWindow *win = ROOT_AS_WINDOW(root); TickitResizeEventInfo *info = _info; int oldlines = win->rect.lines; int oldcols = win->rect.cols; tickit_window_resize(win, info->lines, info->cols); DEBUG_LOGF("Ir", "Resize to %dx%d", info->cols, info->lines); if(info->lines > oldlines) { TickitRect damage = { .top = oldlines, .left = 0, .lines = info->lines - oldlines, .cols = info->cols, }; tickit_window_expose(win, &damage); } if(info->cols > oldcols) { TickitRect damage = { .top = 0, .left = oldcols, .lines = oldlines, .cols = info->cols - oldcols, }; tickit_window_expose(win, &damage); } return 1; } static int on_term_key(TickitTerm *term, TickitEventFlags flags, void *_info, void *user) { TickitRootWindow *root = user; TickitWindow *win = ROOT_AS_WINDOW(root); TickitKeyEventInfo *info = _info; static const char * const evnames[] = { NULL, "KEY", "TEXT" }; DEBUG_LOGF("Ik", "Key event %s %s (mod=%02x)", evnames[info->type], info->str, info->mod); return _handle_key(win, info); } static int on_term_mouse(TickitTerm *term, TickitEventFlags flags, void *_info, void *user) { TickitRootWindow *root = user; TickitWindow *win = ROOT_AS_WINDOW(root); TickitMouseEventInfo *info = _info; static const char * const evnames[] = { NULL, "PRESS", "DRAG", "RELEASE", "WHEEL" }; DEBUG_LOGF("Im", "Mouse event %s %d @%d,%d (mod=%02x)", evnames[info->type], info->button, info->col, info->line, info->mod); if(info->type == TICKIT_MOUSEEV_PRESS) { /* Save the last press location in case of drag */ root->mouse_last_button = info->button; root->mouse_last_line = info->line; root->mouse_last_col = info->col; } else if(info->type == TICKIT_MOUSEEV_DRAG && !root->mouse_dragging) { TickitMouseEventInfo draginfo = { .type = TICKIT_MOUSEEV_DRAG_START, .button = root->mouse_last_button, .line = root->mouse_last_line, .col = root->mouse_last_col, }; root->drag_source_window = _handle_mouse(win, &draginfo); root->mouse_dragging = true; } else if(info->type == TICKIT_MOUSEEV_RELEASE && root->mouse_dragging) { TickitMouseEventInfo draginfo = { .type = TICKIT_MOUSEEV_DRAG_DROP, .button = info->button, .line = info->line, .col = info->col, }; _handle_mouse(win, &draginfo); if(root->drag_source_window) { TickitRect geom = tickit_window_get_abs_geometry(root->drag_source_window); TickitMouseEventInfo draginfo = { .type = TICKIT_MOUSEEV_DRAG_STOP, .button = info->button, .line = info->line - geom.top, .col = info->col - geom.left, }; _handle_mouse(root->drag_source_window, &draginfo); } root->mouse_dragging = false; } TickitWindow *handled = _handle_mouse(win, info); if(info->type == TICKIT_MOUSEEV_DRAG && root->drag_source_window && (!handled || handled != root->drag_source_window)) { TickitRect geom = tickit_window_get_abs_geometry(root->drag_source_window); TickitMouseEventInfo draginfo = { .type = TICKIT_MOUSEEV_DRAG_OUTSIDE, .button = info->button, .line = info->line - geom.top, .col = info->col - geom.left, }; _handle_mouse(root->drag_source_window, &draginfo); } return !!handled; } static void init_window(TickitWindow *win, TickitWindow *parent, TickitRect rect) { win->parent = parent; win->first_child = NULL; win->next = NULL; win->focused_child = NULL; win->pen = tickit_pen_new(); win->rect = rect; win->cursor.line = 0; win->cursor.col = 0; win->cursor.shape = TICKIT_CURSORSHAPE_BLOCK; win->cursor.visible = true; win->cursor.blink = -1; win->is_root = false; win->is_visible = true; win->is_focused = false; win->is_closed = false; win->steal_input = false; win->focus_child_notify = false; win->refcount = 1; win->bindings = (struct TickitBindings){ NULL }; } /* INTERNAL */ TickitWindow* tickit_window_new_root2(Tickit *t, TickitTerm *term) { int lines, cols; tickit_term_get_size(term, &lines, &cols); TickitRootWindow *root = malloc(sizeof(TickitRootWindow)); if(!root) return NULL; init_window(ROOT_AS_WINDOW(root), NULL, (TickitRect) { .top = 0, .left = 0, .lines = lines, .cols = cols }); ROOT_AS_WINDOW(root)->is_root = true; root->term = tickit_term_ref(term); root->hierarchy_changes = NULL; root->needs_expose = false; root->needs_restore = false; root->needs_later_processing = false; root->tickit = t; /* uncounted */ root->damage = tickit_rectset_new(); if(!root->damage) { tickit_window_destroy(ROOT_AS_WINDOW(root)); return NULL; } root->event_ids[0] = tickit_term_bind_event(term, TICKIT_TERM_ON_RESIZE, 0, &on_term_resize, root); root->event_ids[1] = tickit_term_bind_event(term, TICKIT_TERM_ON_KEY, 0, &on_term_key, root); root->event_ids[2] = tickit_term_bind_event(term, TICKIT_TERM_ON_MOUSE, 0, &on_term_mouse, root); root->mouse_dragging = false; tickit_window_expose(ROOT_AS_WINDOW(root), NULL); return ROOT_AS_WINDOW(root); } TickitWindow *tickit_window_new_root(TickitTerm *tt) { return tickit_window_new_root2(NULL, tt); } static TickitRootWindow *_get_root(const TickitWindow *win) { while(!win->is_root) { if(!win->parent) { fprintf(stderr, "tickit_window:_get_root: orphaned window win=%p\n", win); abort(); } win = win->parent; } return WINDOW_AS_ROOT(win); } TickitWindow *tickit_window_new(TickitWindow *parent, TickitRect rect, TickitWindowFlags flags) { if(flags & TICKIT_WINDOW_ROOT_PARENT) while(parent->parent) { rect.top += parent->rect.top; rect.left += parent->rect.left; parent = parent->parent; } TickitWindow *win = malloc(sizeof(TickitWindow)); if(!win) return NULL; init_window(win, parent, rect); if(flags & TICKIT_WINDOW_HIDDEN) win->is_visible = false; if(flags & TICKIT_WINDOW_STEAL_INPUT) win->steal_input = true; _do_hierarchy_change( (flags & TICKIT_WINDOW_LOWEST) ? TICKIT_HIERARCHY_INSERT_LAST : TICKIT_HIERARCHY_INSERT_FIRST, parent, win ); return win; } TickitWindow *tickit_window_parent(const TickitWindow *win) { return win->parent; } TickitWindow *tickit_window_root(const TickitWindow *win) { return ROOT_AS_WINDOW(_get_root(win)); } size_t tickit_window_children(const TickitWindow *win) { size_t ret = 0; for(TickitWindow *child = win->first_child; child; child = child->next) ret++; return ret; } size_t tickit_window_get_children(const TickitWindow *win, TickitWindow *children[], size_t n) { size_t ret = 0; for(TickitWindow *child = win->first_child; ret < n && child; child = child->next) children[ret++] = child; return ret; } TickitTerm *tickit_window_get_term(const TickitWindow *win) { return _get_root(win)->term; } void tickit_window_close(TickitWindow *win) { if(win->parent) _do_hierarchy_change(TICKIT_HIERARCHY_REMOVE, win->parent, win); win->is_closed = true; } void tickit_window_destroy(TickitWindow *win) { tickit_bindings_unbind_and_destroy(&win->bindings, win); if(win->pen) tickit_pen_unref(win->pen); for(TickitWindow *child = win->first_child; child; /**/) { TickitWindow *next = child->next; tickit_window_unref(child); child->parent = NULL; child = next; } if(win->parent) _purge_hierarchy_changes(win); if(!win->is_closed) tickit_window_close(win); /* Root cleanup */ if(win->is_root) { TickitRootWindow *root = WINDOW_AS_ROOT(win); if(root->damage) { tickit_rectset_destroy(root->damage); } tickit_term_unbind_event_id(root->term, root->event_ids[0]); tickit_term_unbind_event_id(root->term, root->event_ids[1]); tickit_term_unbind_event_id(root->term, root->event_ids[2]); tickit_term_unref(root->term); } DEBUG_LOGF("W*", "Window destroyed " WINDOW_PRINTF_FMT, WINDOW_PRINTF_ARGS(win)); free(win); } TickitWindow *tickit_window_ref(TickitWindow *win) { win->refcount++; return win; } void tickit_window_unref(TickitWindow *win) { if(win->refcount < 1) { fprintf(stderr, "tickit_window_unref: invalid refcount %d on win=%p\n", win->refcount, win); abort(); } win->refcount--; if(!win->refcount) tickit_window_destroy(win); } void tickit_window_raise(TickitWindow *win) { _request_hierarchy_change(TICKIT_HIERARCHY_RAISE, win); } void tickit_window_raise_to_front(TickitWindow *win) { _request_hierarchy_change(TICKIT_HIERARCHY_RAISE_FRONT, win); } void tickit_window_lower(TickitWindow *win) { _request_hierarchy_change(TICKIT_HIERARCHY_LOWER, win); } void tickit_window_lower_to_back(TickitWindow *win) { _request_hierarchy_change(TICKIT_HIERARCHY_LOWER_BACK, win); } void tickit_window_show(TickitWindow *win) { win->is_visible = true; if(win->parent) { if(!win->parent->focused_child && (win->focused_child || win->is_focused)) { win->parent->focused_child = win; } } tickit_window_expose(win, NULL); } void tickit_window_hide(TickitWindow *win) { win->is_visible = false; if(win->parent) { TickitWindow *parent = win->parent; if(parent->focused_child && (parent->focused_child == win)) { parent->focused_child = NULL; } tickit_window_expose(parent, &win->rect); } } bool tickit_window_is_visible(TickitWindow *win) { return win->is_visible; } TickitRect tickit_window_get_geometry(const TickitWindow *win) { return win->rect; } TickitRect tickit_window_get_abs_geometry(const TickitWindow *win) { TickitRect geom = win->rect; for(win = win->parent; win; win = win->parent) tickit_rect_translate(&geom, win->rect.top, win->rect.left); return geom; } int tickit_window_bottom(const TickitWindow *win) { return tickit_rect_bottom(&win->rect); } int tickit_window_right(const TickitWindow *win) { return win->rect.left + win->rect.cols; } void tickit_window_resize(TickitWindow *win, int lines, int cols) { tickit_window_set_geometry(win, (TickitRect){ .top = win->rect.top, .left = win->rect.left, .lines = lines, .cols = cols} ); } void tickit_window_reposition(TickitWindow *win, int top, int left) { tickit_window_set_geometry(win, (TickitRect){ .top = top, .left = left, .lines = win->rect.lines, .cols = win->rect.cols} ); if(win->is_focused) _request_restore(_get_root(win)); } void tickit_window_set_geometry(TickitWindow *win, TickitRect geom) { if((win->rect.top != geom.top) || (win->rect.left != geom.left) || (win->rect.lines != geom.lines) || (win->rect.cols != geom.cols)) { TickitGeomchangeEventInfo info = { .rect = geom, .oldrect = win->rect, }; win->rect = geom; run_events(win, TICKIT_WINDOW_ON_GEOMCHANGE, &info); } } TickitPen *tickit_window_get_pen(const TickitWindow *win) { return win->pen; } void tickit_window_set_pen(TickitWindow *win, TickitPen *pen) { if(win->pen) tickit_pen_unref(win->pen); if(pen) win->pen = tickit_pen_ref(pen); else win->pen = NULL; } void tickit_window_expose(TickitWindow *win, const TickitRect *exposed) { TickitRect selfrect = { .top = 0, .left = 0, .lines = win->rect.lines, .cols = win->rect.cols }; TickitRect damaged; if(exposed) { if(!tickit_rect_intersect(&damaged, &selfrect, exposed)) return; } else damaged = selfrect; if(!win->is_visible) return; if(!win->is_root) { if(!win->parent) /* During cleanup at shutdown this might be empty already */ return; tickit_rect_translate(&damaged, win->rect.top, win->rect.left); tickit_window_expose(win->parent, &damaged); return; } DEBUG_LOGF("Wd", "Damage root " RECT_PRINTF_FMT, RECT_PRINTF_ARGS(damaged)); /* If we're here, then we're a root win. */ TickitRootWindow *root = WINDOW_AS_ROOT(win); if(tickit_rectset_contains(root->damage, &damaged)) return; tickit_rectset_add(root->damage, &damaged); root->needs_expose = true; _request_later_processing(root); } static char *_gen_indent(TickitWindow *win) { int depth = 0; while(win->parent) { depth++; win = win->parent; } static size_t buflen = 0; static char *buf = NULL; if(depth * 2 >= buflen) { free(buf); buf = malloc(buflen = (depth * 2 + 1)); buf[0] = 0; } buf[depth*2] = 0; for(char *s = buf; depth; depth--, s += 2) s[0] = '|', s[1] = ' '; return buf; } static void _do_expose(TickitWindow *win, const TickitRect *rect, TickitRenderBuffer *rb) { DEBUG_LOGF("Wx", "%sExpose " WINDOW_PRINTF_FMT " " RECT_PRINTF_FMT, _gen_indent(win), WINDOW_PRINTF_ARGS(win), RECT_PRINTF_ARGS(*rect)); if(win->pen) tickit_renderbuffer_setpen(rb, win->pen); for(TickitWindow* child = win->first_child; child; child = child->next) { if(!child->is_visible) continue; TickitRect exposed; if(tickit_rect_intersect(&exposed, rect, &child->rect)) { tickit_renderbuffer_save(rb); tickit_renderbuffer_clip(rb, &exposed); tickit_renderbuffer_translate(rb, child->rect.top, child->rect.left); tickit_rect_translate(&exposed, -child->rect.top, -child->rect.left); _do_expose(child, &exposed, rb); tickit_renderbuffer_restore(rb); } tickit_renderbuffer_mask(rb, &child->rect); } TickitExposeEventInfo info = { .rect = *rect, .rb = rb, }; run_events(win, TICKIT_WINDOW_ON_EXPOSE, &info); } static void _request_restore(TickitRootWindow *root) { root->needs_restore = true; _request_later_processing(root); } static int _flush_fn(Tickit *t, TickitEventFlags flags, void *info, void *user) { TickitWindow *win = user; tickit_window_flush(win); return 1; } static void _request_later_processing(TickitRootWindow *root) { root->needs_later_processing = true; if(root->tickit) tickit_watch_later(root->tickit, 0, _flush_fn, ROOT_AS_WINDOW(root)); } static bool _cell_visible(TickitWindow *win, int line, int col) { TickitWindow *prev = NULL; while(win) { if(line < 0 || line >= win->rect.lines || col < 0 || col >= win->rect.cols) return false; for(TickitWindow *child = win->first_child; child; child = child->next) { if(prev && child == prev) break; if(!child->is_visible) continue; if(line < child->rect.top || line >= tickit_rect_bottom(&child->rect)) continue; if(col < child->rect.left || col >= tickit_rect_right(&child->rect)) continue; return false; } line += win->rect.top; col += win->rect.left; prev = win; win = win->parent; } return true; } static void _do_restore(TickitRootWindow *root) { TickitWindow *win = ROOT_AS_WINDOW(root); while(win) { if(!win->is_visible) break; if(!win->focused_child) break; win = win->focused_child; } if(win && win->is_focused && win->cursor.visible && _cell_visible(win, win->cursor.line, win->cursor.col)) { TickitRect abs_geom = tickit_window_get_abs_geometry(win); int cursor_line = win->cursor.line + abs_geom.top; int cursor_col = win->cursor.col + abs_geom.left; tickit_term_goto(root->term, cursor_line, cursor_col); tickit_term_setctl_int(root->term, TICKIT_TERMCTL_CURSORSHAPE, win->cursor.shape); if(win->cursor.blink != -1) tickit_term_setctl_int(root->term, TICKIT_TERMCTL_CURSORBLINK, win->cursor.blink); tickit_term_setctl_int(root->term, TICKIT_TERMCTL_CURSORVIS, 1); } else tickit_term_setctl_int(root->term, TICKIT_TERMCTL_CURSORVIS, 0); tickit_term_flush(root->term); } void tickit_window_flush(TickitWindow *win) { if(win->parent) // Can't flush non-root. return; TickitRootWindow *root = WINDOW_AS_ROOT(win); if(!root->needs_later_processing) return; root->needs_later_processing = false; if(root->hierarchy_changes) { HierarchyChange *req = root->hierarchy_changes; while(req) { _do_hierarchy_change(req->change, req->parent, req->win); HierarchyChange *next = req->next; free(req); req = next; } root->hierarchy_changes = NULL; } if(root->needs_expose) { root->needs_expose = false; TickitWindow *root_window = ROOT_AS_WINDOW(root); TickitRenderBuffer *rb = tickit_renderbuffer_new(root_window->rect.lines, root_window->rect.cols); int damage_count = tickit_rectset_rects(root->damage); TickitRect *rects = malloc(damage_count * sizeof(TickitRect)); tickit_rectset_get_rects(root->damage, rects, damage_count); tickit_rectset_clear(root->damage); for(int i = 0; i < damage_count; i++) { TickitRect *rect = &rects[i]; tickit_renderbuffer_save(rb); tickit_renderbuffer_clip(rb, rect); _do_expose(root_window, rect, rb); tickit_renderbuffer_restore(rb); } free(rects); /* Hide terminal cursor during redraw. _do_restore will show it again if * required */ tickit_term_setctl_int(root->term, TICKIT_TERMCTL_CURSORVIS, 0); tickit_renderbuffer_flush_to_term(rb, root->term); tickit_renderbuffer_unref(rb); root->needs_restore = true; } if(root->needs_restore) { root->needs_restore = false; _do_restore(root); } } static TickitWindow **_find_child(TickitWindow *parent, TickitWindow *win) { TickitWindow **winp = &parent->first_child; while(*winp && *winp != win) winp = &(*winp)->next; return winp; } static void _do_hierarchy_insert_first(TickitWindow *parent, TickitWindow *win) { win->next = parent->first_child; parent->first_child = win; } static void _do_hierarchy_insert_last(TickitWindow *parent, TickitWindow *win) { TickitWindow **lastp = &parent->first_child; while(*lastp) lastp = &(*lastp)->next; *lastp = win; win->next = NULL; } static void _do_hierarchy_remove(TickitWindow *parent, TickitWindow *win) { TickitWindow **winp = _find_child(parent, win); if(!winp) return; *winp = (*winp)->next; win->next = NULL; } static void _do_hierarchy_raise(TickitWindow *parent, TickitWindow *win) { TickitWindow **prevp = &parent->first_child; if(*prevp == win) // already first return; while(*prevp && (*prevp)->next != win) prevp = &(*prevp)->next; if(!prevp) // not found return; TickitWindow *after = win->next; win->next = *prevp; (*prevp)->next = after; *prevp = win; } static void _do_hierarchy_lower(TickitWindow *parent, TickitWindow *win) { TickitWindow **winp = _find_child(parent, win); if(!winp) // not found return; TickitWindow *after = win->next; if(!after) // already last return; win->next = after->next; *winp = after; after->next = win; } static void _do_hierarchy_change(HierarchyChangeType change, TickitWindow *parent, TickitWindow *win) { const char *fmt = NULL; switch(change) { case TICKIT_HIERARCHY_INSERT_FIRST: fmt = "Window " WINDOW_PRINTF_FMT " adds " WINDOW_PRINTF_FMT; _do_hierarchy_insert_first(parent, win); break; case TICKIT_HIERARCHY_INSERT_LAST: fmt = "Window " WINDOW_PRINTF_FMT " adds " WINDOW_PRINTF_FMT; _do_hierarchy_insert_last(parent, win); break; case TICKIT_HIERARCHY_REMOVE: fmt = "Window " WINDOW_PRINTF_FMT " removes " WINDOW_PRINTF_FMT; _do_hierarchy_remove(parent, win); win->parent = NULL; if(parent->focused_child && parent->focused_child == win) parent->focused_child = NULL; break; case TICKIT_HIERARCHY_RAISE: fmt = "Window " WINDOW_PRINTF_FMT " raises " WINDOW_PRINTF_FMT; _do_hierarchy_raise(parent, win); break; case TICKIT_HIERARCHY_RAISE_FRONT: fmt = "Window " WINDOW_PRINTF_FMT " raises " WINDOW_PRINTF_FMT " to front"; _do_hierarchy_remove(parent, win); _do_hierarchy_insert_first(parent, win); break; case TICKIT_HIERARCHY_LOWER: fmt = "Window " WINDOW_PRINTF_FMT " lowers " WINDOW_PRINTF_FMT; _do_hierarchy_lower(parent, win); break; case TICKIT_HIERARCHY_LOWER_BACK: fmt = "Window " WINDOW_PRINTF_FMT " lowers " WINDOW_PRINTF_FMT " to back"; _do_hierarchy_remove(parent, win); _do_hierarchy_insert_last(parent, win); break; } if(fmt) DEBUG_LOGF("Wh", fmt, WINDOW_PRINTF_ARGS(parent), WINDOW_PRINTF_ARGS(win)); if(win->is_visible) tickit_window_expose(parent, &win->rect); } static void _request_hierarchy_change(HierarchyChangeType change, TickitWindow *win) { if(!win->parent) /* Can't do anything to the root win */ return; HierarchyChange *req = malloc(sizeof(HierarchyChange)); req->change = change; req->parent = win->parent; req->win = win; req->next = NULL; TickitRootWindow *root = _get_root(win); if(!root->hierarchy_changes) { root->hierarchy_changes = req; _request_later_processing(root); } else { HierarchyChange *chain = root->hierarchy_changes; while(chain->next) { chain = chain->next; } chain->next = req; } } static void _purge_hierarchy_changes(TickitWindow *win) { TickitRootWindow *root = _get_root(win); HierarchyChange **changep = &root->hierarchy_changes; while(*changep) { HierarchyChange *req = *changep; if(req->parent == win || req->win == win) { *changep = req->next; free(req); } else changep = &req->next; } } static bool _scrollrectset(TickitWindow *win, TickitRectSet *visible, int downward, int rightward, TickitPen *pen) { TickitWindow *origwin = win; int abs_top = 0, abs_left = 0; while(win) { if(!win->is_visible) return false; if(win->pen) tickit_pen_copy(pen, win->pen, false); TickitWindow *parent = win->parent; if(!parent) break; abs_top += win->rect.top; abs_left += win->rect.left; tickit_rectset_translate(visible, win->rect.top, win->rect.left); for(TickitWindow *sib = parent->first_child; sib; sib = sib->next) { if(sib == win) break; if(!sib->is_visible) continue; tickit_rectset_subtract(visible, &sib->rect); } win = parent; } TickitTerm *term = WINDOW_AS_ROOT(win)->term; int n = tickit_rectset_rects(visible); TickitRect rects[n]; tickit_rectset_get_rects(visible, rects, n); bool ret = true; bool done_pen = false; for(int i = 0; i < n; i++) { TickitRect rect = rects[i]; TickitRect origrect = rect; tickit_rect_translate(&origrect, -abs_top, -abs_left); if(abs(downward) >= rect.lines || abs(rightward) >= rect.cols) { tickit_window_expose(origwin, &origrect); continue; } // TODO: This may be more efficiently done with some rectset operations // instead of completely resetting and rebuilding the set TickitRectSet *damageset = WINDOW_AS_ROOT(win)->damage; int n_damage = tickit_rectset_rects(damageset); TickitRect damage[n_damage]; tickit_rectset_get_rects(damageset, damage, n_damage); tickit_rectset_clear(damageset); for(int j = 0; j < n_damage; j++) { TickitRect r = damage[j]; if(tickit_rect_bottom(&r) < rect.top || r.top > tickit_rect_bottom(&rect) || tickit_rect_right(&r) < rect.left || r.left > tickit_rect_right(&rect)) { tickit_rectset_add(damageset, &r); continue; } TickitRect outside[4]; int n_outside; if((n_outside = tickit_rect_subtract(outside, &r, &rect))) { for(int k = 0; k < n_outside; k++) tickit_rectset_add(damageset, outside+k); } TickitRect inside; if(tickit_rect_intersect(&inside, &r, &rect)) { tickit_rect_translate(&inside, -downward, -rightward); if(tickit_rect_intersect(&inside, &inside, &rect)) tickit_rectset_add(damageset, &inside); } } DEBUG_LOGF("Wsr", "Term scrollrect " RECT_PRINTF_FMT " by %+d,%+d", RECT_PRINTF_ARGS(rect), rightward, downward); if(!done_pen) { // TODO: only bg matters tickit_term_setctl_int(term, TICKIT_TERMCTL_CURSORVIS, 0); tickit_term_setpen(term, pen); done_pen = true; } if(tickit_term_scrollrect(term, rect, downward, rightward)) { if(downward > 0) { // "scroll down" means lines moved upward, so the bottom needs redrawing tickit_window_expose(origwin, &(TickitRect){ .top = tickit_rect_bottom(&origrect) - downward, .lines = downward, .left = origrect.left, .cols = rect.cols }); } else if(downward < 0) { // "scroll up" means lines moved downward, so top needs redrawing tickit_window_expose(origwin, &(TickitRect){ .top = origrect.top, .lines = -downward, .left = origrect.left, .cols = rect.cols, }); } if(rightward > 0) { // "scroll right" means columns moved leftward, so the right edge needs redrawing tickit_window_expose(origwin, &(TickitRect){ .top = origrect.top, .lines = rect.lines, .left = tickit_rect_right(&origrect) - rightward, .cols = rightward, }); } else if(rightward < 0) { tickit_window_expose(origwin, &(TickitRect){ .top = origrect.top, .lines = rect.lines, .left = origrect.left, .cols = -rightward, }); } } else { tickit_window_expose(origwin, &origrect); ret = false; } } if(done_pen) { _request_restore(WINDOW_AS_ROOT(win)); } return ret; } static bool _scroll(TickitWindow *win, const TickitRect *origrect, int downward, int rightward, TickitPen *pen, bool mask_children) { TickitRect rect; TickitRect selfrect = { .top = 0, .left = 0, .lines = win->rect.lines, .cols = win->rect.cols }; if(!tickit_rect_intersect(&rect, &selfrect, origrect)) return false; DEBUG_LOGF("Ws", "Scroll " RECT_PRINTF_FMT " by %+d,%+d", RECT_PRINTF_ARGS(rect), rightward, downward); if(pen) pen = tickit_pen_ref(pen); else pen = tickit_pen_new(); TickitRectSet *visible = tickit_rectset_new(); tickit_rectset_add(visible, &rect); if(mask_children) for(TickitWindow *child = win->first_child; child; child = child->next) { if(!child->is_visible) continue; tickit_rectset_subtract(visible, &child->rect); } bool ret = _scrollrectset(win, visible, downward, rightward, pen); tickit_rectset_destroy(visible); tickit_pen_unref(pen); return ret; } bool tickit_window_scrollrect(TickitWindow *win, const TickitRect *rect, int downward, int rightward, TickitPen *pen) { return _scroll(win, rect, downward, rightward, pen, true); } bool tickit_window_scroll(TickitWindow *win, int downward, int rightward) { return _scroll(win, &((TickitRect){ .top = 0, .left = 0, .lines = win->rect.lines, .cols = win->rect.cols }), downward, rightward, NULL, true); } bool tickit_window_scroll_with_children(TickitWindow *win, int downward, int rightward) { return _scroll(win, &((TickitRect){ .top = 0, .left = 0, .lines = win->rect.lines, .cols = win->rect.cols }), downward, rightward, NULL, false); } void tickit_window_set_cursor_position(TickitWindow *win, int line, int col) { win->cursor.line = line; win->cursor.col = col; if(win->is_focused) _request_restore(_get_root(win)); } void tickit_window_set_cursor_visible(TickitWindow *win, bool visible) { tickit_window_setctl_int(win, TICKIT_WINCTL_CURSORVIS, visible); } void tickit_window_set_cursor_shape(TickitWindow *win, TickitCursorShape shape) { tickit_window_setctl_int(win, TICKIT_WINCTL_CURSORSHAPE, shape); } static void _focus_gained(TickitWindow *win, TickitWindow *child); static void _focus_lost(TickitWindow *win); void tickit_window_take_focus(TickitWindow *win) { _focus_gained(win, NULL); } static void _focus_gained(TickitWindow *win, TickitWindow *child) { if(win->focused_child && child && win->focused_child != child) _focus_lost(win->focused_child); if(win->parent) { if(win->is_visible) _focus_gained(win->parent, win); } else _request_restore(_get_root(win)); if(!child) { win->is_focused = true; TickitFocusEventInfo info = { .type = TICKIT_FOCUSEV_IN, .win = win }; run_events(win, TICKIT_WINDOW_ON_FOCUS, &info); } else if(win->focus_child_notify) { TickitFocusEventInfo info = { .type = TICKIT_FOCUSEV_IN, .win = child }; run_events(win, TICKIT_WINDOW_ON_FOCUS, &info); } win->focused_child = child; } static void _focus_lost(TickitWindow *win) { if(win->focused_child) { _focus_lost(win->focused_child); if(win->focus_child_notify) { TickitFocusEventInfo info = { .type = TICKIT_FOCUSEV_OUT, .win = win->focused_child }; run_events(win, TICKIT_WINDOW_ON_FOCUS, &info); } } if(win->is_focused) { win->is_focused = false; TickitFocusEventInfo info = { .type = TICKIT_FOCUSEV_OUT, .win = win }; run_events(win, TICKIT_WINDOW_ON_FOCUS, &info); } } bool tickit_window_is_focused(const TickitWindow *win) { return win->is_focused; } void tickit_window_set_focus_child_notify(TickitWindow *win, bool notify) { tickit_window_setctl_int(win, TICKIT_WINCTL_FOCUS_CHILD_NOTIFY, notify); } bool tickit_window_is_steal_input(const TickitWindow *win) { return win->steal_input; } void tickit_window_set_steal_input(TickitWindow *win, bool steal) { tickit_window_setctl_int(win, TICKIT_WINCTL_STEAL_INPUT, steal); } bool tickit_window_getctl_int(TickitWindow *win, TickitWindowCtl ctl, int *value) { switch(ctl) { case TICKIT_WINCTL_STEAL_INPUT: *value = win->steal_input; return true; case TICKIT_WINCTL_FOCUS_CHILD_NOTIFY: *value = win->focus_child_notify; return true; case TICKIT_WINCTL_CURSORVIS: *value = win->cursor.visible; return true; case TICKIT_WINCTL_CURSORBLINK: *value = win->cursor.blink; return true; case TICKIT_WINCTL_CURSORSHAPE: *value = win->cursor.shape; return true; case TICKIT_N_WINCTLS: ; } return false; } bool tickit_window_setctl_int(TickitWindow *win, TickitWindowCtl ctl, int value) { switch(ctl) { case TICKIT_WINCTL_STEAL_INPUT: win->steal_input = value; return true; case TICKIT_WINCTL_FOCUS_CHILD_NOTIFY: win->focus_child_notify = value; return true; case TICKIT_WINCTL_CURSORVIS: win->cursor.visible = value; goto restore; case TICKIT_WINCTL_CURSORBLINK: win->cursor.blink = value ? 1 : 0; goto restore; case TICKIT_WINCTL_CURSORSHAPE: win->cursor.shape = value; goto restore; case TICKIT_N_WINCTLS: ; } return false; restore: if(win->is_focused) _request_restore(_get_root(win)); return true; } static int _handle_key(TickitWindow *win, TickitKeyEventInfo *info) { if(!win->is_visible) return 0; int ret = 1; tickit_window_ref(win); if(win->first_child && win->first_child->steal_input) if(_handle_key(win->first_child, info)) goto done; if(win->focused_child) if(_handle_key(win->focused_child, info)) goto done; if(run_events_whilefalse(win, TICKIT_WINDOW_ON_KEY, info)) goto done; // Last-ditch attempt to spread it around other children TickitWindow *next; for(TickitWindow *child = win->first_child; child; child = next) { next = child->next; if(child == win->focused_child) continue; if(_handle_key(child, info)) goto done; } ret = 0; /* fallthough */ done: tickit_window_unref(win); return ret; } static TickitWindow *_handle_mouse(TickitWindow *win, TickitMouseEventInfo *info) { if(!win->is_visible) return NULL; TickitWindow *ret; tickit_window_ref(win); TickitWindow *next; for(TickitWindow *child = win->first_child; child; child = next) { next = child->next; int child_line = info->line - child->rect.top; int child_col = info->col - child->rect.left; if(!child->steal_input) { if(child_line < 0 || child_line >= child->rect.lines) continue; if(child_col < 0 || child_col >= child->rect.cols) continue; } TickitMouseEventInfo childinfo = *info; childinfo.line = child_line; childinfo.col = child_col; if((ret = _handle_mouse(child, &childinfo))) goto done; } ret = win; if(run_events_whilefalse(win, TICKIT_WINDOW_ON_MOUSE, info)) goto done; ret = NULL; /* fallthrough */ done: tickit_window_unref(win); return ret; } const char *tickit_window_ctlname(TickitWindowCtl ctl) { switch(ctl) { case TICKIT_WINCTL_STEAL_INPUT: return "steal-input"; case TICKIT_WINCTL_FOCUS_CHILD_NOTIFY: return "focus-child-notify"; case TICKIT_WINCTL_CURSORVIS: return "cursor-visible"; case TICKIT_WINCTL_CURSORBLINK: return "cursor-blink"; case TICKIT_WINCTL_CURSORSHAPE: return "cursor-shape"; case TICKIT_N_WINCTLS: ; } return NULL; } TickitWindowCtl tickit_window_lookup_ctl(const char *name) { const char *s; for(TickitWindowCtl ctl = 1; ctl < TICKIT_N_WINCTLS; ctl++) if((s = tickit_window_ctlname(ctl)) && streq(name, s)) return ctl; return -1; } TickitType tickit_window_ctltype(TickitWindowCtl ctl) { switch(ctl) { case TICKIT_WINCTL_STEAL_INPUT: case TICKIT_WINCTL_FOCUS_CHILD_NOTIFY: case TICKIT_WINCTL_CURSORVIS: case TICKIT_WINCTL_CURSORBLINK: return TICKIT_TYPE_BOOL; case TICKIT_WINCTL_CURSORSHAPE: return TICKIT_TYPE_INT; case TICKIT_N_WINCTLS: ; } return TICKIT_TYPE_NONE; } libtickit-0.3.4/src/xterm-palette.inc0000644000000000000000000000657113613430267015715 0ustar 00000000000000static struct { unsigned int as16 : 4; unsigned int as8 : 3; } xterm256[] = { // 0 - 7 { 0, 0 }, { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, // 8 - 15 { 8, 3 }, { 9, 1 }, { 10, 2 }, { 11, 3 }, { 12, 4 }, { 13, 5 }, { 14, 6 }, { 15, 7 }, // 16 - 23 { 0, 0 }, { 0, 0 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 0, 0 }, { 0, 0 }, // 24 - 31 { 6, 6 }, { 4, 4 }, { 4, 4 }, { 12, 4 }, { 2, 2 }, { 2, 2 }, { 6, 6 }, { 6, 6 }, // 32 - 39 { 6, 6 }, { 6, 6 }, { 2, 2 }, { 2, 2 }, { 6, 6 }, { 6, 6 }, { 6, 6 }, { 6, 6 }, // 40 - 47 { 2, 2 }, { 2, 2 }, { 6, 6 }, { 6, 6 }, { 6, 6 }, { 14, 6 }, { 10, 2 }, { 10, 2 }, // 48 - 55 { 6, 6 }, { 6, 6 }, { 14, 6 }, { 14, 6 }, { 0, 0 }, { 0, 0 }, { 5, 5 }, { 4, 4 }, // 56 - 63 { 4, 4 }, { 12, 4 }, { 0, 0 }, { 8, 0 }, { 8, 5 }, { 8, 4 }, { 12, 4 }, { 12, 4 }, // 64 - 71 { 2, 2 }, { 8, 2 }, { 8, 6 }, { 8, 6 }, { 12, 6 }, { 12, 6 }, { 2, 2 }, { 8, 2 }, // 72 - 79 { 8, 6 }, { 8, 6 }, { 12, 6 }, { 12, 6 }, { 2, 2 }, { 8, 2 }, { 8, 6 }, { 6, 6 }, // 80 - 87 { 6, 6 }, { 14, 6 }, { 10, 2 }, { 10, 2 }, { 6, 6 }, { 6, 6 }, { 14, 6 }, { 14, 6 }, // 88 - 95 { 1, 1 }, { 1, 1 }, { 5, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 }, { 1, 1 }, { 8, 1 }, // 96 - 103 { 8, 5 }, { 8, 5 }, { 12, 5 }, { 12, 5 }, { 3, 3 }, { 8, 3 }, { 8, 7 }, { 8, 7 }, // 104 - 111 { 12, 7 }, { 12, 7 }, { 3, 3 }, { 8, 3 }, { 8, 7 }, { 8, 7 }, { 8, 7 }, { 12, 7 }, // 112 - 119 { 3, 3 }, { 8, 3 }, { 8, 7 }, { 8, 7 }, { 7, 7 }, { 7, 7 }, { 3, 3 }, { 3, 3 }, // 120 - 127 { 8, 7 }, { 7, 7 }, { 7, 7 }, { 7, 7 }, { 1, 1 }, { 1, 1 }, { 5, 5 }, { 5, 5 }, // 128 - 135 { 5, 5 }, { 5, 5 }, { 1, 1 }, { 8, 1 }, { 8, 5 }, { 8, 5 }, { 12, 5 }, { 12, 5 }, // 136 - 143 { 3, 3 }, { 8, 3 }, { 8, 7 }, { 8, 7 }, { 8, 7 }, { 12, 7 }, { 3, 3 }, { 8, 3 }, // 144 - 151 { 8, 7 }, { 8, 7 }, { 7, 7 }, { 7, 7 }, { 3, 3 }, { 3, 3 }, { 8, 7 }, { 7, 7 }, // 152 - 159 { 7, 7 }, { 7, 7 }, { 3, 3 }, { 3, 3 }, { 7, 7 }, { 7, 7 }, { 7, 7 }, { 7, 7 }, // 160 - 167 { 1, 1 }, { 1, 1 }, { 5, 5 }, { 5, 5 }, { 5, 5 }, { 13, 5 }, { 1, 1 }, { 8, 1 }, // 168 - 175 { 8, 5 }, { 5, 5 }, { 5, 5 }, { 13, 5 }, { 3, 3 }, { 8, 3 }, { 8, 7 }, { 8, 7 }, // 176 - 183 { 7, 7 }, { 7, 7 }, { 3, 3 }, { 3, 3 }, { 8, 7 }, { 7, 7 }, { 7, 7 }, { 7, 7 }, // 184 - 191 { 3, 3 }, { 3, 3 }, { 7, 7 }, { 7, 7 }, { 7, 7 }, { 7, 7 }, { 11, 3 }, { 11, 3 }, // 192 - 199 { 7, 7 }, { 7, 7 }, { 7, 7 }, { 7, 7 }, { 9, 1 }, { 9, 1 }, { 5, 5 }, { 5, 5 }, // 200 - 207 { 13, 5 }, { 13, 5 }, { 9, 1 }, { 9, 1 }, { 5, 5 }, { 5, 5 }, { 13, 5 }, { 13, 5 }, // 208 - 215 { 3, 3 }, { 3, 3 }, { 8, 7 }, { 7, 7 }, { 7, 7 }, { 7, 7 }, { 3, 3 }, { 3, 3 }, // 216 - 223 { 7, 7 }, { 7, 7 }, { 7, 7 }, { 7, 7 }, { 11, 3 }, { 11, 3 }, { 7, 7 }, { 7, 7 }, // 224 - 231 { 7, 7 }, { 7, 7 }, { 11, 3 }, { 11, 3 }, { 7, 7 }, { 7, 7 }, { 7, 7 }, { 15, 7 }, // 232 - 239 { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 8, 0 }, { 8, 0 }, // 240 - 247 { 8, 0 }, { 8, 0 }, { 8, 0 }, { 8, 7 }, { 8, 7 }, { 8, 7 }, { 8, 7 }, { 8, 7 }, // 248 - 255 { 8, 7 }, { 8, 7 }, { 7, 7 }, { 7, 7 }, { 7, 7 }, { 7, 7 }, { 7, 7 }, { 7, 7 }, }; libtickit-0.3.4/src/xterm-palette.inc.PL0000644000000000000000000000165213613430267016222 0ustar 00000000000000#!/usr/bin/perl use strict; use warnings; use Convert::Color; use Convert::Color::XTerm; use List::UtilsBy qw( min_by ); print <new( $_ ) } 0 .. 15; my @XTerm8 = @XTerm16[0..7]; foreach my $index ( 0 .. 255 ) { my $col_rgb = Convert::Color::XTerm->new( $index )->as_rgb; if( $index == 232 ) { # Once we get to the greys at the end, restrict the lists down to # only grey colours @XTerm16 = @XTerm16[0,7,8,15]; @XTerm8 = @XTerm8[0,7]; } my $col16 = min_by { $col_rgb->dst_rgb_cheap( $_->as_rgb ) } @XTerm16; my $col8 = min_by { $col_rgb->dst_rgb_cheap( $_->as_rgb ) } @XTerm8; printf " // %d - %d\n ", $index, $index + 7 if $index % 8 == 0; printf "{ %2d, %d }, ", $col16->index, $col8->index; print "\n" if $index % 8 == 7; } print "};\n"; libtickit-0.3.4/t/01utf8.c0000644000000000000000000001522613613430267013273 0ustar 00000000000000#include "tickit.h" #include "taplib.h" int main(int argc, char *argv[]) { TickitStringPos pos, limit; is_int(tickit_utf8_count("hello", &pos, NULL), 5, "tickit_utf8_count ASCII"); is_int(pos.bytes, 5, "tickit_utf8_count ASCII bytes"); is_int(pos.codepoints, 5, "tickit_utf8_count ASCII codepoints"); is_int(pos.graphemes, 5, "tickit_utf8_count ASCII graphemes"); is_int(pos.columns, 5, "tickit_utf8_count ASCII columns"); /* U+00E9 - LATIN SMALL LETTER E WITH ACUTE * 0xc3 0xa9 */ is_int(tickit_utf8_count("caf\xc3\xa9", &pos, NULL), 5, "tickit_utf8_count UTF-8"); is_int(pos.bytes, 5, "tickit_utf8_count UTF-8 bytes"); is_int(pos.codepoints, 4, "tickit_utf8_count UTF-8 codepoints"); is_int(pos.graphemes, 4, "tickit_utf8_count UTF-8 graphemes"); is_int(pos.columns, 4, "tickit_utf8_count UTF-8 columns"); /* U+0301 - COMBINING ACUTE ACCENT * 0xcc 0x81 */ is_int(tickit_utf8_count("cafe\xcc\x81", &pos, NULL), 6, "tickit_utf8_count UTF-8 combining"); is_int(pos.bytes, 6, "tickit_utf8_count UTF-8 combining bytes"); is_int(pos.codepoints, 5, "tickit_utf8_count UTF-8 combining codepoints"); is_int(pos.graphemes, 4, "tickit_utf8_count UTF-8 combining graphemes"); is_int(pos.columns, 4, "tickit_utf8_count UTF-8 combining columns"); /* U+5F61 - CJK UNIFIED IDEOGRAPH-5F61 * 0xe5 0xbd 0xa1 */ is_int(tickit_utf8_count("\xe5\xbd\xa1", &pos, NULL), 3, "tickit_utf8_count UTF-8 CJK"); is_int(pos.bytes, 3, "tickit_utf8_count UTF-8 CJK bytes"); is_int(pos.codepoints, 1, "tickit_utf8_count UTF-8 CJK codepoints"); is_int(pos.graphemes, 1, "tickit_utf8_count UTF-8 CJK graphemes"); is_int(pos.columns, 2, "tickit_utf8_count UTF-8 CJK columns"); /* U+FF21 - FULLWIDTH LATIN CAPITAL LETTER A * 0xef 0xbc 0xa1 */ is_int(tickit_utf8_count("\xef\xbc\xa1", &pos, NULL), 3, "tickit_utf8_count UTF-8 fullwidth"); is_int(pos.bytes, 3, "tickit_utf8_count UTF-8 fullwidth bytes"); is_int(pos.codepoints, 1, "tickit_utf8_count UTF-8 fullwidth codepoints"); is_int(pos.graphemes, 1, "tickit_utf8_count UTF-8 fullwidth graphemes"); is_int(pos.columns, 2, "tickit_utf8_count UTF-8 fullwidth columns"); /* And now a nice long string */ is_int(tickit_utf8_count("(\xe3\x83\x8e\xe0\xb2\x0a\xe7\xb2\xa0)\xe3\x83\x8e\xe5\xbd\xa1\xe2\x94\xbb\xe2\x94\x81\xe2\x94\xbb", &pos, NULL), 26, "tickit_utf8_count UTF-8 string"); is_int(pos.bytes, 26, "tickit_utf8_count UTF-8 string bytes"); is_int(pos.codepoints, 10, "tickit_utf8_count UTF-8 string codepoints"); is_int(pos.graphemes, 10, "tickit_utf8_count UTF-8 string graphemes"); is_int(pos.columns, 14, "tickit_utf8_count UTF-8 string columns"); /* Now with some limits */ tickit_stringpos_limit_bytes(&limit, 5); is_int(tickit_utf8_count("hello world", &pos, &limit), 5, "tickit_utf8_count byte-limit"); is_int(pos.bytes, 5, "tickit_utf8_count byte-limit bytes"); is_int(pos.codepoints, 5, "tickit_utf8_count byte-limit codepoints"); is_int(pos.graphemes, 5, "tickit_utf8_count byte-limit graphemes"); is_int(pos.columns, 5, "tickit_utf8_count byte-limit columns"); /* check byte limit never chops UTF-8 codepoints */ limit.bytes = 4; is_int(tickit_utf8_count("caf\xc3\xa9", &pos, &limit), 3, "tickit_utf8_count byte-limit split"); is_int(pos.bytes, 3, "tickit_utf8_count byte-limit split bytes"); tickit_stringpos_limit_codepoints(&limit, 3); is_int(tickit_utf8_count("hello world", &pos, &limit), 3, "tickit_utf8_count char-limit"); is_int(pos.bytes, 3, "tickit_utf8_count char-limit bytes"); is_int(pos.codepoints, 3, "tickit_utf8_count char-limit codepoints"); is_int(pos.graphemes, 3, "tickit_utf8_count char-limit graphemes"); is_int(pos.columns, 3, "tickit_utf8_count char-limit columns"); /* check char limit never chops graphemes */ limit.codepoints = 4; is_int(tickit_utf8_count("cafe\xcc\x81", &pos, &limit), 3, "tickit_utf8_count char-limit split"); is_int(pos.codepoints, 3, "tickit_utf8_count char-limit split codepoints"); tickit_stringpos_limit_graphemes(&limit, 4); is_int(tickit_utf8_count("hello world", &pos, &limit), 4, "tickit_utf8_count grapheme-limit"); is_int(pos.bytes, 4, "tickit_utf8_count grapheme-limit bytes"); is_int(pos.codepoints, 4, "tickit_utf8_count grapheme-limit codepoints"); is_int(pos.graphemes, 4, "tickit_utf8_count grapheme-limit graphemes"); is_int(pos.columns, 4, "tickit_utf8_count grapheme-limit columns"); tickit_stringpos_limit_columns(&limit, 6); is_int(tickit_utf8_count("hello world", &pos, &limit), 6, "tickit_utf8_count column-limit"); is_int(pos.bytes, 6, "tickit_utf8_count column-limit bytes"); is_int(pos.codepoints, 6, "tickit_utf8_count column-limit codepoints"); is_int(pos.graphemes, 6, "tickit_utf8_count column-limit graphemes"); is_int(pos.columns, 6, "tickit_utf8_count column-limit columns"); /* check column limit never chops graphemes */ limit.columns = 2; is_int(tickit_utf8_count("A\xef\xbc\xa1", &pos, &limit), 1, "tickit_utf8_count column-limit split"); is_int(pos.columns, 1, "tickit_utf8_count column-limit split grapheme"); /* countmore should continue where count left off */ tickit_stringpos_limit_columns(&limit, 3); tickit_utf8_count("cafe\xcc\x81", &pos, &limit); limit.columns++; tickit_utf8_countmore("cafe\xcc\x81", &pos, &limit); is_int(pos.bytes, 6, "tickit_utf8_countmore continues after count"); /* ncount */ tickit_stringpos_limit_none(&limit); tickit_utf8_ncount("ABCDEF", 3, &pos, &limit); is_int(pos.bytes, 3, "tickit_utf8_ncount stops at len"); /* ncount should commit on the first codepoint even though there's the start of another one after it */ tickit_stringpos_limit_none(&limit); tickit_utf8_ncount("a\xcc", 1, &pos, &limit); is_int(pos.bytes, 1, "tickit_utf8_ncount ignores partial UTF-8 past len"); /* ncount still stops on NUL bytes */ is_int(tickit_utf8_ncount("ABCDEF", 7, &pos, &limit), 6, "tickit_utf8_ncount stops at NUL"); is_int(pos.bytes, 6, "tickit_utf8_ncount indicates end at NUL"); /* C0 and C1 controls and ASCII DEL are errors */ tickit_stringpos_limit_none(&limit); is_int(tickit_utf8_count("\x1b", &pos, &limit), -1, "tickit_utf8_count -1 for C0"); is_int(tickit_utf8_count("\x9b", &pos, &limit), -1, "tickit_utf8_count -1 for C0"); is_int(tickit_utf8_count("\x7f", &pos, &limit), -1, "tickit_utf8_count -1 for DEL"); /* convenience utilities */ is_int(tickit_utf8_mbswidth("caf\xc3\xa9 time"), 9, "tickit_utf8_mbswidth"); is_int(tickit_utf8_byte2col("caf\xc3\xa9 time", 7), 6, "tickit_utf8_byte2col"); is_int(tickit_utf8_col2byte("caf\xc3\xa9 time", 6), 7, "tickit_utf8_col2byte"); return exit_status(); } libtickit-0.3.4/t/02pen.c0000644000000000000000000002016013613430267013161 0ustar 00000000000000#include "tickit.h" #include "taplib.h" static int on_event_incr(TickitPen *pen, TickitEventFlags flags, void *_info, void *data) { ((int *)data)[0]++; return 1; } static int arr[2]; static int next_arr = 0; static int on_event_push(TickitPen *pen, TickitEventFlags flags, void *_info, void *data) { arr[next_arr++] = *(int *)data; return 1; } int main(int argc, char *argv[]) { TickitPen *pen, *pen2; pen = tickit_pen_new(); ok(!!pen, "tickit_pen_new"); int changed = 0; tickit_pen_bind_event(pen, TICKIT_PEN_ON_CHANGE, 0, on_event_incr, &changed); is_int(tickit_pen_attrtype(TICKIT_PEN_BOLD), TICKIT_PENTYPE_BOOL, "bold is a boolean attribute"); is_int(tickit_pen_lookup_attr("b"), TICKIT_PEN_BOLD, "lookup_attr \"b\" gives bold"); is_str(tickit_pen_attrname(TICKIT_PEN_BOLD), "b", "pen_attrname bold gives \"b\""); is_int(changed, 0, "change counter 0 initially"); ok(!tickit_pen_has_attr(pen, TICKIT_PEN_BOLD), "pen lacks bold initially"); ok(!tickit_pen_nondefault_attr(pen, TICKIT_PEN_BOLD), "pen bold attr is default initially"); is_int(tickit_pen_get_bool_attr(pen, TICKIT_PEN_BOLD), 0, "bold 0 initially"); ok(!tickit_pen_is_nonempty(pen), "pen initially empty"); ok(!tickit_pen_is_nondefault(pen), "pen initially default"); tickit_pen_set_bool_attr(pen, TICKIT_PEN_BOLD, 1); ok(tickit_pen_has_attr(pen, TICKIT_PEN_BOLD), "pen has bold after set"); ok(tickit_pen_nondefault_attr(pen, TICKIT_PEN_BOLD), "pen bold attr is nondefault after set"); is_int(tickit_pen_get_bool_attr(pen, TICKIT_PEN_BOLD), 1, "bold 1 after set"); ok(tickit_pen_is_nonempty(pen), "pen non-empty after set bold on"); ok(tickit_pen_is_nondefault(pen), "pen non-default after set bold on"); is_int(changed, 1, "change counter 1 after set bold on"); tickit_pen_set_bool_attr(pen, TICKIT_PEN_BOLD, 0); ok(!tickit_pen_nondefault_attr(pen, TICKIT_PEN_BOLD), "pen bold attr is default after set bold off"); ok(tickit_pen_is_nonempty(pen), "pen non-empty after set bold off"); ok(!tickit_pen_is_nondefault(pen), "pen default after set bold off"); is_int(changed, 2, "change counter 2 after set bold off"); tickit_pen_clear_attr(pen, TICKIT_PEN_BOLD); ok(!tickit_pen_has_attr(pen, TICKIT_PEN_BOLD), "pen lacks bold after clear"); is_int(tickit_pen_get_bool_attr(pen, TICKIT_PEN_BOLD), 0, "bold 0 after clear"); is_int(changed, 3, "change counter 3 after clear bold"); is_int(tickit_pen_attrtype(TICKIT_PEN_FG), TICKIT_PENTYPE_COLOUR, "foreground is a colour attribute"); ok(!tickit_pen_has_attr(pen, TICKIT_PEN_FG), "pen lacks foreground initially"); is_int(tickit_pen_get_colour_attr(pen, TICKIT_PEN_FG), -1, "foreground -1 initially"); tickit_pen_set_colour_attr(pen, TICKIT_PEN_FG, 4); ok(tickit_pen_has_attr(pen, TICKIT_PEN_FG), "pen has foreground after set"); is_int(tickit_pen_get_colour_attr(pen, TICKIT_PEN_FG), 4, "foreground 4 after set"); ok(tickit_pen_set_colour_attr_desc(pen, TICKIT_PEN_FG, "12"), "pen set foreground '12'"); is_int(tickit_pen_get_colour_attr(pen, TICKIT_PEN_FG), 12, "foreground 12 after set '12'"); ok(tickit_pen_set_colour_attr_desc(pen, TICKIT_PEN_FG, "green"), "pen set foreground 'green'"); is_int(tickit_pen_get_colour_attr(pen, TICKIT_PEN_FG), 2, "foreground 2 after set 'green'"); ok(tickit_pen_set_colour_attr_desc(pen, TICKIT_PEN_FG, "hi-red"), "pen set foreground 'hi-red'"); is_int(tickit_pen_get_colour_attr(pen, TICKIT_PEN_FG), 8+1, "foreground 8+1 after set 'hi-red'"); tickit_pen_clear_attr(pen, TICKIT_PEN_FG); ok(!tickit_pen_has_attr(pen, TICKIT_PEN_FG), "pen lacks foreground after clear"); is_int(tickit_pen_get_colour_attr(pen, TICKIT_PEN_FG), -1, "foreground -1 after clear"); pen2 = tickit_pen_new(); ok(tickit_pen_equiv_attr(pen, pen2, TICKIT_PEN_BOLD), "pens have equiv bold attribute initially"); tickit_pen_set_bool_attr(pen, TICKIT_PEN_BOLD, 1); ok(!tickit_pen_equiv_attr(pen, pen2, TICKIT_PEN_BOLD), "pens have unequiv bold attribute after set"); ok(tickit_pen_equiv_attr(pen, pen2, TICKIT_PEN_ITALIC), "pens have equiv italic attribute"); tickit_pen_set_bool_attr(pen, TICKIT_PEN_ITALIC, 0); ok(tickit_pen_equiv_attr(pen, pen2, TICKIT_PEN_ITALIC), "pens have equiv italic attribute after set 0"); tickit_pen_copy_attr(pen2, pen, TICKIT_PEN_BOLD); ok(tickit_pen_equiv_attr(pen, pen2, TICKIT_PEN_BOLD), "pens have equiv bold attribute after copy attr"); tickit_pen_clear_attr(pen2, TICKIT_PEN_BOLD); tickit_pen_copy(pen2, pen, 1); ok(tickit_pen_equiv_attr(pen, pen2, TICKIT_PEN_BOLD), "pens have equiv bold attribute after copy"); tickit_pen_set_bool_attr(pen2, TICKIT_PEN_BOLD, 0); tickit_pen_copy(pen2, pen, 0); ok(!tickit_pen_equiv_attr(pen, pen2, TICKIT_PEN_BOLD), "pens have non-equiv bold attribute after copy no overwrite"); tickit_pen_set_int_attr(pen, TICKIT_PEN_UNDER, TICKIT_PEN_UNDER_NONE); tickit_pen_clear_attr(pen2, TICKIT_PEN_UNDER); tickit_pen_copy(pen2, pen, 1); ok(tickit_pen_has_attr(pen2, TICKIT_PEN_UNDER), "pen copy still copies present but default-value attributes"); is_ptr(tickit_pen_ref(pen), pen, "tickit_pen_ref() returns same pen"); tickit_pen_bind_event(pen, TICKIT_PEN_ON_DESTROY, 0, on_event_push, (int []){1}); tickit_pen_bind_event(pen, TICKIT_PEN_ON_DESTROY, 0, on_event_push, (int []){2}); tickit_pen_unref(pen); ok(!next_arr, "pen not destroyed after first unref"); tickit_pen_unref(pen); is_int(next_arr, 2, "pen destroyed after second unref"); is_int(arr[0]*10 + arr[1], 21, "TICKIT_EV_DESTROY runs in reverse order"); tickit_pen_unref(pen2); pen = tickit_pen_new_attrs(TICKIT_PEN_BOLD, 1, TICKIT_PEN_FG, 3, 0); is_int(tickit_pen_get_bool_attr(pen, TICKIT_PEN_BOLD), 1, "pen bold attr for new_attrs"); is_int(tickit_pen_get_colour_attr(pen, TICKIT_PEN_FG), 3, "pen fg attr for new_attrs"); tickit_pen_unref(pen); // UNDER bool back-compat { TickitPen *pen = tickit_pen_new(); ok(!tickit_pen_has_attr(pen, TICKIT_PEN_UNDER), "default pen has no UNDER"); is_int(tickit_pen_get_int_attr(pen, TICKIT_PEN_UNDER), TICKIT_PEN_UNDER_NONE, "default pen UNDER is 0"); ok(!tickit_pen_get_bool_attr(pen, TICKIT_PEN_UNDER), "default pen has no UNDER bool"); tickit_pen_set_int_attr(pen, TICKIT_PEN_UNDER, TICKIT_PEN_UNDER_DOUBLE); ok(tickit_pen_has_attr(pen, TICKIT_PEN_UNDER), "pen has UNDER"); is_int(tickit_pen_get_int_attr(pen, TICKIT_PEN_UNDER), TICKIT_PEN_UNDER_DOUBLE, "pen UNDER is DOUBLE"); ok(tickit_pen_get_bool_attr(pen, TICKIT_PEN_UNDER), "pen has UNDER bool"); tickit_pen_unref(pen); } // RGB8 secondary colour attributes { TickitPen *pen = tickit_pen_new(); TickitPenRGB8 val; ok(!tickit_pen_has_colour_attr_rgb8(pen, TICKIT_PEN_FG), "new pen does not have FG RGB8"); tickit_pen_set_colour_attr(pen, TICKIT_PEN_FG, 15); ok(!tickit_pen_has_colour_attr_rgb8(pen, TICKIT_PEN_FG), "pen still does not have FG RGB after set index"); tickit_pen_set_colour_attr_rgb8(pen, TICKIT_PEN_FG, (TickitPenRGB8){230, 240, 250}); ok(tickit_pen_has_colour_attr_rgb8(pen, TICKIT_PEN_FG), "pen now has FG RGB8 after set RGB8"); val = tickit_pen_get_colour_attr_rgb8(pen, TICKIT_PEN_FG); is_int(val.r, 230, "val.r from get RGB8"); is_int(val.g, 240, "val.g from get RGB8"); is_int(val.b, 250, "val.b from get RGB8"); TickitPen *pen2 = tickit_pen_clone(pen); ok(tickit_pen_has_colour_attr_rgb8(pen2, TICKIT_PEN_FG), "tickit_pen_clone preserves RGB8 attributes"); ok(tickit_pen_equiv(pen, pen2), "cloned pen is equivalent"); tickit_pen_clear_attr(pen2, TICKIT_PEN_FG); tickit_pen_set_colour_attr(pen2, TICKIT_PEN_FG, 15); ok(!tickit_pen_equiv(pen, pen2), "pen not equivalent after clearing RGB8"); tickit_pen_set_colour_attr(pen, TICKIT_PEN_FG, 20); ok(!tickit_pen_has_colour_attr_rgb8(pen, TICKIT_PEN_FG), "pen does not have FG RGB after set new index"); tickit_pen_set_colour_attr_rgb8(pen, TICKIT_PEN_BG, (TickitPenRGB8){10, 20, 30}); ok(!tickit_pen_has_colour_attr_rgb8(pen, TICKIT_PEN_BG), "pen does not have BG RGB8 with no index"); tickit_pen_unref(pen2); tickit_pen_unref(pen); } return exit_status(); } libtickit-0.3.4/t/03rect.c0000644000000000000000000002422013613430267013336 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include "taplib-tickit.h" #include int main(int argc, char *argv[]) { TickitRect rect1, rect2, rectOut; tickit_rect_init_sized(&rect1, 5, 10, 7, 20); is_int(rect1.top, 5, "rect1.top"); is_int(rect1.left, 10, "rect1.left"); is_int(rect1.lines, 7, "rect1.lines"); is_int(rect1.cols, 20, "rect1.cols"); is_int(tickit_rect_bottom(&rect1), 12, "tickit_rect_bottom(rect1)"); is_int(tickit_rect_right(&rect1), 30, "tickit_rect_right(rect1)"); tickit_rect_init_sized(&rect2, 0, 0, 25, 80); tickit_rect_intersect(&rectOut, &rect1, &rect2); is_int(rectOut.top, 5, "rectOut.top from intersect wholescreen"); is_int(rectOut.left, 10, "rectOut.left from intersect wholescreen"); is_int(rectOut.lines, 7, "rectOut.lines from intersect wholescreen"); is_int(rectOut.cols, 20, "rectOut.cols from intersect wholescreen"); is_int(tickit_rect_bottom(&rectOut), 12, "tickit_rect_bottom(rectOut) from intersect wholescreen"); is_int(tickit_rect_right(&rectOut), 30, "tickit_rect_right(rectOut) from intersect wholescreen"); tickit_rect_init_sized(&rect2, 10, 20, 15, 60); tickit_rect_intersect(&rectOut, &rect1, &rect2); is_int(rectOut.top, 10, "rectOut.top from intersect partial"); is_int(rectOut.left, 20, "rectOut.left from intersect partial"); is_int(rectOut.lines, 2, "rectOut.lines from intersect partial"); is_int(rectOut.cols, 10, "rectOut.cols from intersect partial"); is_int(tickit_rect_bottom(&rectOut), 12, "tickit_rect_bottom(rectOut) from intersect partial"); is_int(tickit_rect_right(&rectOut), 30, "tickit_rect_right(rectOut) from intersect partial"); tickit_rect_init_sized(&rect2, 20, 20, 5, 60); ok(!tickit_rect_intersect(&rectOut, &rect1, &rect2), "false from intersect outside"); tickit_rect_init_sized(&rect2, 7, 12, 3, 10); ok(tickit_rect_contains(&rect1, &rect2), "tickit_rect_contains() for smaller"); tickit_rect_init_sized(&rect2, 3, 10, 5, 12); ok(!tickit_rect_contains(&rect1, &rect2), "tickit_rect_contains() for overlap"); tickit_rect_init_sized(&rect2, 3, 10, 5, 12); ok(tickit_rect_intersects(&rect1, &rect2), "tickit_rect_intersects() with overlap"); tickit_rect_init_sized(&rect2, 14, 10, 3, 20); ok(!tickit_rect_intersects(&rect1, &rect2), "tickit_rect_intersects() with other"); tickit_rect_init_sized(&rect2, 12, 10, 3, 20); ok(!tickit_rect_intersects(&rect1, &rect2), "tickit_rect_intersects() with abutting"); tickit_rect_init_bounded(&rect1, 3, 8, 9, 22); is_int(rect1.top, 3, "rect1.top from init_bounded"); is_int(rect1.left, 8, "rect1.left from init_bounded"); is_int(rect1.lines, 6, "rect1.lines from init_bounded"); is_int(rect1.cols, 14, "rect1.cols from init_bounded"); is_int(tickit_rect_bottom(&rect1), 9, "tickit_rect_bottom(rect1) from init_bounded"); is_int(tickit_rect_right(&rect1), 22, "tickit_rect_right(rect1) from init_bounded"); tickit_rect_translate(&rect1, 2, 4); is_int(rect1.top, 5, "rect1.top after translate"); is_int(rect1.left, 12, "rect1.left after translate"); is_int(rect1.lines, 6, "rect1.lines after translate"); is_int(rect1.cols, 14, "rect1.cols after translate"); // Rectangle addition TickitRect rects[4]; int n_rects; rect_init_strp(&rect1, "10,10..20,20"); n_rects = tickit_rect_add(rects, &rect1, rect_init_strp(&rect2, "10,10..20,20")); is_int(n_rects, 1, "rect_add same"); is_rect(rects + 0, "10,10..20,20", "rects[0] for rect_add same"); n_rects = tickit_rect_add(rects, &rect1, rect_init_strp(&rect2, "5,10..10,20")); is_int(n_rects, 1, "rect_add left"); is_rect(rects + 0, "5,10..20,20", "rects[0] for rect_add left"); n_rects = tickit_rect_add(rects, &rect1, rect_init_strp(&rect2, "20,10..25,20")); is_int(n_rects, 1, "rect_add right"); is_rect(rects + 0, "10,10..25,20", "rects[0] for rect_add right"); n_rects = tickit_rect_add(rects, &rect1, rect_init_strp(&rect2, "10,5..20,10")); is_int(n_rects, 1, "rect_add top"); is_rect(rects + 0, "10,5..20,20", "rects[0] for rect_add top"); n_rects = tickit_rect_add(rects, &rect1, rect_init_strp(&rect2, "10,20..20,25")); is_int(n_rects, 1, "rect_add bottom"); is_rect(rects + 0, "10,10..20,25", "rects[0] for rect_add bottom"); n_rects = tickit_rect_add(rects, &rect1, rect_init_strp(&rect2, "12,5..18,10")); is_int(n_rects, 2, "rect_add T above"); is_rect(rects + 0, "12,5..18,10", "rects[0] for rect_add T above"); is_rect(rects + 1, "10,10..20,20", "rects[1] for rect_add T above"); n_rects = tickit_rect_add(rects, &rect1, rect_init_strp(&rect2, "12,20..18,30")); is_int(n_rects, 2, "rect_add T below"); is_rect(rects + 0, "10,10..20,20", "rects[0] for rect_add T below"); is_rect(rects + 1, "12,20..18,30", "rects[1] for rect_add T below"); n_rects = tickit_rect_add(rects, &rect1, rect_init_strp(&rect2, "5,12..10,18")); is_int(n_rects, 3, "rect_add T left"); is_rect(rects + 0, "10,10..20,12", "rects[0] for rect_add T left"); is_rect(rects + 1, "5,12..20,18", "rects[1] for rect_add T left"); is_rect(rects + 2, "10,18..20,20", "rects[2] for rect_add T left"); n_rects = tickit_rect_add(rects, &rect1, rect_init_strp(&rect2, "20,12..25,18")); is_int(n_rects, 3, "rect_add T right"); is_rect(rects + 0, "10,10..20,12", "rects[0] for rect_add T right"); is_rect(rects + 1, "10,12..25,18", "rects[1] for rect_add T right"); is_rect(rects + 2, "10,18..20,20", "rects[2] for rect_add T right"); n_rects = tickit_rect_add(rects, &rect1, rect_init_strp(&rect2, "15,15..25,25")); is_int(n_rects, 3, "rect_add diagonal"); is_rect(rects + 0, "10,10..20,15", "rects[0] for rect_add diagonal"); is_rect(rects + 1, "10,15..25,20", "rects[1] for rect_add diagonal"); is_rect(rects + 2, "15,20..25,25", "rects[2] for rect_add diagonal"); n_rects = tickit_rect_add(rects, &rect1, rect_init_strp(&rect2, "12,8..18,22")); is_int(n_rects, 3, "rect_add cross"); is_rect(rects + 0, "12,8..18,10", "rects[0] for rect_add cross"); is_rect(rects + 1, "10,10..20,20", "rects[1] for rect_add cross"); is_rect(rects + 2, "12,20..18,22", "rects[2] for rect_add cross"); n_rects = tickit_rect_add(rects, &rect1, rect_init_strp(&rect2, "10,30..20,40")); is_int(n_rects, 2, "rect_add non-overlap horizontal"); is_rect(rects + 0, "10,10..20,20", "rects[0] for rect_add non-overlap horizontal"); is_rect(rects + 1, "10,30..20,40", "rects[1] for rect_add non-overlap horizontal"); n_rects = tickit_rect_add(rects, &rect1, rect_init_strp(&rect2, "30,10..40,20")); is_int(n_rects, 2, "rect_add non-overlap vertical"); is_rect(rects + 0, "10,10..20,20", "rects[0] for rect_add non-overlap vertical"); is_rect(rects + 1, "30,10..40,20", "rects[1] for rect_add non-overlap vertical"); // Rectangle subtraction n_rects = tickit_rect_subtract(rects, &rect1, rect_init_strp(&rect2, "10,10..20,20")); is_int(n_rects, 0, "rect_subtract self"); n_rects = tickit_rect_subtract(rects, &rect1, rect_init_strp(&rect2, "5,10..15,20")); is_int(n_rects, 1, "rect_subtract truncate left"); is_rect(rects + 0, "15,10..20,20", "rects[0] for rect_subtract truncate left"); n_rects = tickit_rect_subtract(rects, &rect1, rect_init_strp(&rect2, "15,10..25,20")); is_int(n_rects, 1, "rect_subtract truncate right"); is_rect(rects + 0, "10,10..15,20", "rects[0] for rect_subtract truncate right"); n_rects = tickit_rect_subtract(rects, &rect1, rect_init_strp(&rect2, "10,5..20,15")); is_int(n_rects, 1, "rect_subtract truncate top"); is_rect(rects + 0, "10,15..20,20", "rects[0] for rect_subtract truncate top"); n_rects = tickit_rect_subtract(rects, &rect1, rect_init_strp(&rect2, "10,15..20,25")); is_int(n_rects, 1, "rect_subtract truncate bottom"); is_rect(rects + 0, "10,10..20,15", "rects[0] for rect_subtract truncate bottom"); n_rects = tickit_rect_subtract(rects, &rect1, rect_init_strp(&rect2, "5,12..25,18")); is_int(n_rects, 2, "rect_subtract slice horizontal"); is_rect(rects + 0, "10,10..20,12", "rects[0] for rect_subtract slice horizontal"); is_rect(rects + 1, "10,18..20,20", "rects[1] for rect_subtract slice horizontal"); n_rects = tickit_rect_subtract(rects, &rect1, rect_init_strp(&rect2, "12,5..18,25")); is_int(n_rects, 2, "rect_subtract slice vertical"); is_rect(rects + 0, "10,10..12,20", "rects[0] for rect_subtract slice vertical"); is_rect(rects + 1, "18,10..20,20", "rects[1] for rect_subtract slice vertical"); n_rects = tickit_rect_subtract(rects, &rect1, rect_init_strp(&rect2, "5,12..15,18")); is_int(n_rects, 3, "rect_subtract U left"); is_rect(rects + 0, "10,10..20,12", "rects[0] for rect_subtract U left"); is_rect(rects + 1, "15,12..20,18", "rects[1] for rect_subtract U left"); is_rect(rects + 2, "10,18..20,20", "rects[2] for rect_subtract U left"); n_rects = tickit_rect_subtract(rects, &rect1, rect_init_strp(&rect2, "15,12..25,18")); is_int(n_rects, 3, "rect_subtract U right"); is_rect(rects + 0, "10,10..20,12", "rects[0] for rect_subtract U right"); is_rect(rects + 1, "10,12..15,18", "rects[1] for rect_subtract U right"); is_rect(rects + 2, "10,18..20,20", "rects[2] for rect_subtract U right"); n_rects = tickit_rect_subtract(rects, &rect1, rect_init_strp(&rect2, "12,5..18,15")); is_int(n_rects, 3, "rect_subtract U top"); is_rect(rects + 0, "10,10..12,15", "rects[0] for rect_subtract U top"); is_rect(rects + 1, "18,10..20,15", "rects[1] for rect_subtract U top"); is_rect(rects + 2, "10,15..20,20", "rects[2] for rect_subtract U top"); n_rects = tickit_rect_subtract(rects, &rect1, rect_init_strp(&rect2, "12,15..18,25")); is_int(n_rects, 3, "rect_subtract U bottom"); is_rect(rects + 0, "10,10..20,15", "rects[0] for rect_subtract U bottom"); is_rect(rects + 1, "10,15..12,20", "rects[1] for rect_subtract U bottom"); is_rect(rects + 2, "18,15..20,20", "rects[2] for rect_subtract U bottom"); n_rects = tickit_rect_subtract(rects, &rect1, rect_init_strp(&rect2, "12,12..18,18")); is_int(n_rects, 4, "rect_subtract hole"); is_rect(rects + 0, "10,10..20,12", "rects[0] for rect_subtract hole"); is_rect(rects + 1, "10,12..12,18", "rects[1] for rect_subtract hole"); is_rect(rects + 2, "18,12..20,18", "rects[2] for rect_subtract hole"); is_rect(rects + 3, "10,18..20,20", "rects[3] for rect_subtract hole"); return exit_status(); } libtickit-0.3.4/t/04rectset.c0000644000000000000000000002354013613430267014057 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include "taplib-tickit.h" #include #include int rects_init_strp(TickitRect *rects, int n, const char *str) { int i = 0; while(str && *str && i < n) { rect_init_strp(rects + i, str); i++; str = strchr(str, ' '); if(!str) break; while(*str == ' ') str++; } return i; } void test_add(const char *input, const char *output) { TickitRectSet *trs = tickit_rectset_new(); TickitRect inputs[4]; int n_inputs = rects_init_strp(inputs, 4, input); TickitRect exp_outputs[4]; int n_exp_outputs = rects_init_strp(exp_outputs, 4, output); int reverse; for(reverse = 0; reverse < 2; reverse++) { tickit_rectset_clear(trs); int i; for(i = 0; i < n_inputs; i++) tickit_rectset_add(trs, inputs + (reverse ? n_inputs - i - 1 : i)); int n_got_outputs = tickit_rectset_rects(trs); TickitRect got_outputs[n_got_outputs]; tickit_rectset_get_rects(trs, got_outputs, n_got_outputs); for(i = 0; i < n_exp_outputs; i++) { if(i >= n_got_outputs) { diag("Received too many rects"); goto fail_dir; } TickitRect *got = got_outputs + i; TickitRect *exp = exp_outputs + i; if(got->top != exp->top || got->left != exp->left || got->lines != exp->lines || got->cols != exp->cols) { diag("got %d,%d..%d,%d expected %d,%d..%d,%d", got->left, got->top, tickit_rect_right(got), tickit_rect_bottom(got), exp->left, exp->top, tickit_rect_right(exp), tickit_rect_bottom(exp)); goto fail_dir; } } if(i < n_got_outputs) { diag("Received insufficient rects"); goto fail_dir; } pass(reverse ? "tickit_rectset_add reversed" : "tickit_rectset_add"); continue; fail_dir: fail(reverse ? "tickit_rectset_add reversed" : "tickit_rectset_add"); } tickit_rectset_destroy(trs); } void test_subtract(const char *input, const char *output) { TickitRectSet *trs = tickit_rectset_new(); TickitRect inputs[4]; int n_inputs = rects_init_strp(inputs, 4, input); tickit_rectset_add(trs, inputs + 0); int i; for(i = 1; i < n_inputs; i++) tickit_rectset_subtract(trs, inputs + i); TickitRect exp_outputs[4]; int n_exp_outputs = rects_init_strp(exp_outputs, 4, output); int n_got_outputs = tickit_rectset_rects(trs); TickitRect got_outputs[n_got_outputs]; tickit_rectset_get_rects(trs, got_outputs, n_got_outputs); for(i = 0; i < n_exp_outputs; i++) { if(i >= n_got_outputs) { diag("Received too many rects"); goto fail; } TickitRect *got = got_outputs + i; TickitRect *exp = exp_outputs + i; if(got->top != exp->top || got->left != exp->left || got->lines != exp->lines || got->cols != exp->cols) { diag("got %d,%d..%d,%d expected %d,%d..%d,%d", got->left, got->top, tickit_rect_right(got), tickit_rect_bottom(got), exp->left, exp->top, tickit_rect_right(exp), tickit_rect_bottom(exp)); goto fail; } } if(i < n_got_outputs) { diag("Received insufficient rects"); goto fail; } pass("tickit_rectset_subtract"); tickit_rectset_destroy(trs); return; fail: fail("tickit_rectset_subtract"); tickit_rectset_destroy(trs); } int main(int argc, char *argv[]) { { TickitRectSet *trs = tickit_rectset_new(); TickitRect tmp; TickitRect rects[4]; ok(!!trs, "tickit_rectset_new"); is_int(tickit_rectset_rects(trs), 0, "tickit_rectset_rects initially"); is_int(tickit_rectset_get_rects(trs, rects, 4), 0, "tickit_rectset_get_rects initially"); // Distinct regions tickit_rectset_add(trs, rect_init_strp(&tmp, "10,10+20,5")); is_int(tickit_rectset_get_rect(trs, 0, &tmp), 1, "tickit_rectset_get_rect after tickit_rectset_Add"); is_rect(&tmp, "10,10+20,5", "tmp after tickit_rectset_get_rect"); is_int(tickit_rectset_get_rects(trs, rects, 4), 1, "tickit_rectset_get_rects after tickit_rectset_add"); is_rect(rects + 0, "10,10+20,5", "rects[0] after tickit_rectset_add"); tickit_rectset_add(trs, rect_init_strp(&tmp, "10,20+20,2")); is_int(tickit_rectset_get_rects(trs, rects, 4), 2, "tickit_rectset_get_rects after second tickit_rectset_add"); is_rect(rects + 0, "10,10+20,5", "rects[0] after second tickit_rectset_add"); is_rect(rects + 1, "10,20+20,2", "rects[1] after second tickit_rectset_add"); // Translation tickit_rectset_translate(trs, 2, 3); is_int(tickit_rectset_get_rects(trs, rects, 4), 2, "tickit_rectset_get_rects after second tickit_rectset_translate"); is_rect(rects + 0, "13,12+20,5", "rects[0] after second tickit_rectset_translate"); is_rect(rects + 1, "13,22+20,2", "rects[1] after second tickit_rectset_translate"); tickit_rectset_clear(trs); is_int(tickit_rectset_rects(trs), 0, "tickit_rectset_rects after clear"); // Intersect and containment tickit_rectset_add(trs, rect_init_strp(&tmp, "1,1..20,5")); tickit_rectset_add(trs, rect_init_strp(&tmp, "1,5..10,10")); ok( tickit_rectset_intersects(trs, rect_init_strp(&tmp, "0,0..5,5")), "intersects overlap"); ok(!tickit_rectset_intersects(trs, rect_init_strp(&tmp, "15,6..25,9")), "doesn't intersect no overlap"); ok( tickit_rectset_contains(trs, rect_init_strp(&tmp, "5,1..15,4")), "contains simple"); ok( tickit_rectset_contains(trs, rect_init_strp(&tmp, "5,2..8,9")), "contains split"); ok(!tickit_rectset_contains(trs, rect_init_strp(&tmp, "5,2..12,9")), "doesn't contain split"); ok(!tickit_rectset_contains(trs, rect_init_strp(&tmp, "15,6..25,9")), "doesn't contain non-intersect"); tickit_rectset_destroy(trs); } // Distinct regions test_add("10,10..30,15 40,10..60,15", "10,10..30,15 40,10..60,15"); test_add("10,10..30,15 10,20..30,25", "10,10..30,15 10,20..30,25"); // Ignorable regions test_add("10,10..30,15 10,10..30,15", "10,10..30,15"); test_add("10,10..30,15 10,10..20,12", "10,10..30,15"); test_add("10,10..30,15 20,13..30,15", "10,10..30,15"); test_add("10,10..30,15 15,11..25,14", "10,10..30,15"); // Overlapping extension top test_add("10,10..30,15 10,8..30,12", "10,8..30,15"); test_add("10,10..30,15 10,8..30,10", "10,8..30,15"); test_add("10,10..30,12 10,15..30,17 10,12..30,15", "10,10..30,17"); // Overlapping extension bottom test_add("10,10..30,15 10,12..30,17", "10,10..30,17"); test_add("10,10..30,15 10,15..30,17", "10,10..30,17"); // Overlapping extension left test_add("10,10..30,15 5,10..25,15", "5,10..30,15"); test_add("10,10..30,15 5,10..10,15", "5,10..30,15"); // Overlapping extension right test_add("10,10..30,15 20,10..35,15", "10,10..35,15"); test_add("10,10..30,15 30,10..35,15", "10,10..35,15"); // L/T shape top abutting test_add("10,10..30,15 10,8..20,10", "10,8..20,10 10,10..30,15"); test_add("10,10..30,15 15,8..25,10", "15,8..25,10 10,10..30,15"); test_add("10,10..30,15 20,8..30,10", "20,8..30,10 10,10..30,15"); // L/T shape top overlapping test_add("10,10..30,15 10,8..20,12", "10,8..20,10 10,10..30,15"); test_add("10,10..30,15 15,8..25,12", "15,8..25,10 10,10..30,15"); test_add("10,10..30,15 20,8..30,12", "20,8..30,10 10,10..30,15"); // L/T shape bottom abutting test_add("10,10..30,15 10,15..20,17", "10,10..30,15 10,15..20,17"); test_add("10,10..30,15 15,15..25,17", "10,10..30,15 15,15..25,17"); test_add("10,10..30,15 20,15..30,17", "10,10..30,15 20,15..30,17"); // L/T shape bottom overlapping test_add("10,10..30,15 10,13..20,17", "10,10..30,15 10,15..20,17"); test_add("10,10..30,15 15,13..25,17", "10,10..30,15 15,15..25,17"); test_add("10,10..30,15 20,13..30,17", "10,10..30,15 20,15..30,17"); // L/T shape left abutting test_add("10,10..30,15 5,10..10,12", "5,10..30,12 10,12..30,15"); test_add("10,10..30,15 5,11..10,14", "10,10..30,11 5,11..30,14 10,14..30,15"); test_add("10,10..30,15 5,13..10,15", "10,10..30,13 5,13..30,15"); // L/T shape left overlapping test_add("10,10..30,15 5,10..15,12", "5,10..30,12 10,12..30,15"); test_add("10,10..30,15 5,11..15,14", "10,10..30,11 5,11..30,14 10,14..30,15"); test_add("10,10..30,15 5,13..15,15", "10,10..30,13 5,13..30,15"); // L/T shape right abutting test_add("10,10..30,15 30,10..35,12", "10,10..35,12 10,12..30,15"); test_add("10,10..30,15 30,11..35,14", "10,10..30,11 10,11..35,14 10,14..30,15"); test_add("10,10..30,15 30,13..35,15", "10,10..30,13 10,13..35,15"); // L/T shape right overlapping test_add("10,10..30,15 20,10..35,12", "10,10..35,12 10,12..30,15"); test_add("10,10..30,15 20,11..35,14", "10,10..30,11 10,11..35,14 10,14..30,15"); test_add("10,10..30,15 20,13..35,15", "10,10..30,13 10,13..35,15"); // Cross shape test_add("10,10..30,15 15,5..25,20", "15,5..25,10 10,10..30,15 15,15..25,20"); // Diagonal overlap test_add("10,10..30,15 20,12..40,20", "10,10..30,12 10,12..40,15 20,15..40,20"); test_add("10,10..30,15 0,12..20,20", "10,10..30,12 0,12..30,15 0,15..20,20"); // Distinct regions test_subtract("10,10..30,15 10,20..30,22", "10,10..30,15"); // Overlapping truncate left test_subtract("10,10..30,15 5,10..20,15", "20,10..30,15"); // Overlapping truncate right test_subtract("10,10..30,15 20,10..35,15", "10,10..20,15"); // Overlapping truncate top test_subtract("10,10..30,15 10,8..30,12", "10,12..30,15"); // Overlapping truncate bottom test_subtract("10,10..30,15 10,13..30,18", "10,10..30,13"); // Overlapping U left test_subtract("10,10..30,15 5,11..20,14", "10,10..30,11 20,11..30,14 10,14..30,15"); // Overlapping U right test_subtract("10,10..30,15 20,11..35,14", "10,10..30,11 10,11..20,14 10,14..30,15"); // Overlapping U top test_subtract("10,10..30,15 15,8..25,12", "10,10..15,12 25,10..30,12 10,12..30,15"); // Overlapping U bottom test_subtract("10,10..30,15 15,13..25,18", "10,10..30,13 10,13..15,15 25,13..30,15"); // Remove entirely test_subtract("10,10..30,15 8,8..32,17", ""); return exit_status(); } libtickit-0.3.4/t/05string.c0000644000000000000000000000104613613430267013712 0ustar 00000000000000#include "tickit.h" #include "taplib.h" int main(int argc, char *argv[]) { TickitString *s; s = tickit_string_new("Hello, world!", 13); ok(!!s, "tickit_string_new"); is_str(tickit_string_get(s), "Hello, world!", "tickit_string_get"); is_int(tickit_string_len(s), 13, "tickit_string_len"); { is_ptr(tickit_string_ref(s), s, "tickit_string_ref"); tickit_string_unref(s); is_str(tickit_string_get(s), "Hello, world!", "tickit_string_get after tickit_string_unref"); } tickit_string_unref(s); return exit_status(); } libtickit-0.3.4/t/08bindings.c0000644000000000000000000000255413613430267014211 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include "../src/bindings.h" struct TickitBindings bindings = { NULL }; int incr(void *owner, TickitEventFlags flags, void *info, void *data) { ((int *)data)[0]++; return 0; } int delete(void *owner, TickitEventFlags flags, void *info, void *data) { tickit_bindings_unbind_event_id(&bindings, NULL, *(int *)info); return 0; } int main(int argc, char *argv[]) { { int id = tickit_bindings_bind_event(&bindings, NULL, 1<<0, 0, &delete, NULL); int count = 0; tickit_bindings_bind_event(&bindings, NULL, 1<<0, 0, &incr, &count); tickit_bindings_run_event(&bindings, NULL, 1<<0, &id); is_int(count, 1, "binding after self-removal still invoked"); tickit_bindings_unbind_and_destroy(&bindings, NULL); bindings.first = NULL; } { tickit_bindings_bind_event(&bindings, NULL, 1<<1, 0, &delete, NULL); int count2 = 0; int id2 = tickit_bindings_bind_event(&bindings, NULL, 1<<1, 0, &incr, &count2); int count3 = 0; tickit_bindings_bind_event(&bindings, NULL, 1<<1, 0, &incr, &count3); tickit_bindings_run_event(&bindings, NULL, 1<<1, &id2); is_int(count2, 0, "removed binding is not invoked"); is_int(count3, 1, "binding after removed one still invoked"); tickit_bindings_unbind_and_destroy(&bindings, NULL); bindings.first = NULL; } return exit_status(); } libtickit-0.3.4/t/09debug.c0000644000000000000000000000206513613430267013500 0ustar 00000000000000#ifdef __GLIBC__ # define _XOPEN_SOURCE 500 // strdup #endif #include "tickit.h" #include "taplib.h" #include #include char *msg = NULL; void savemsg(const char *str, void *data) { if(msg) free(msg); msg = strdup(str); } int main(int argc, char *argv[]) { putenv("TICKIT_DEBUG_FLAGS=T,X,Ca"); tickit_debug_set_func(savemsg, NULL); tickit_debug_init(); tickit_debug_logf("T", "Test message %s", "here"); ok(!!msg, "tickit_debug_logf saves a message"); // First 12 characters are timestamp is_str(msg+12, " [T ]: Test message here\n", "message string"); if(msg) { free(msg); msg = NULL; } tickit_debug_logf("A", "Another message"); ok(!msg, "tickit_debug_logf does not save a message with nonmatching flag"); if(msg) { free(msg); msg = NULL; } tickit_debug_logf("Ca", "Submatch pass"); ok(!!msg, "tickit_debug_logf logs on submatch"); if(msg) { free(msg); msg = NULL; } tickit_debug_logf("Cb", "Submatch fail"); ok(!msg, "tickit_debug_logf does not log failing submatch"); return exit_status(); } libtickit-0.3.4/t/10term-write.c0000644000000000000000000000516613613430267014506 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include #include bool output_eof = false; void output(TickitTerm *tt, const char *bytes, size_t len, void *user) { char *buffer = user; if(bytes) strncat(buffer, bytes, len); else output_eof = true; } int main(int argc, char *argv[]) { TickitTerm *tt; int fd[2]; char buffer[1024] = { 0 }; size_t len; /* We'll need a real filehandle we can write/read. * pipe() can make us one */ pipe(fd); tt = tickit_term_new_for_termtype("xterm"); ok(!!tt, "tickit_term_new_for_termtype"); tickit_term_set_output_fd(tt, fd[1]); is_int(tickit_term_get_output_fd(tt), fd[1], "tickit_term_get_output_fd"); /* Already it should have written its DECSLRM probe string */ len = read(fd[0], buffer, sizeof buffer); buffer[len] = 0; is_str_escape(buffer, "\e[?69h\e[?69$p\e[?25$p\e[?12$p\eP$q q\e\\" "\e[38;5;255m\e[38:2:0:1:2m\eP$qm\e\\\e[m" "\e[G\e[K", "buffer after initialisation contains DECSLRM and cursor status probes"); tickit_term_print(tt, "Hello world!"); len = read(fd[0], buffer, sizeof buffer); buffer[len] = 0; is_int(len, 12, "read() length after tickit_term_print"); is_str_escape(buffer, "Hello world!", "buffer after tickit_term_print"); tickit_term_printn(tt, "another string here", 7); len = read(fd[0], buffer, sizeof buffer); buffer[len] = 0; is_int(len, 7, "read() length after tickit_term_printn"); is_str_escape(buffer, "another", "buffer after tickit_term_printn"); tickit_term_printf(tt, "%s %s!", "More", "messages"); len = read(fd[0], buffer, sizeof buffer); buffer[len] = 0; is_int(len, 14, "read() length after tickit_term_printf"); is_str_escape(buffer, "More messages!", "buffer after tickit_term_printf"); tickit_term_goto(tt, 2, 5); len = read(fd[0], buffer, sizeof buffer); buffer[len] = 0; is_str_escape(buffer, "\e[3;6H", "buffer after tickit_term_goto line+col"); tickit_term_goto(tt, 4, -1); len = read(fd[0], buffer, sizeof buffer); buffer[len] = 0; is_str_escape(buffer, "\e[5d", "buffer after tickit_term_goto line"); tickit_term_goto(tt, -1, 10); len = read(fd[0], buffer, sizeof buffer); buffer[len] = 0; is_str_escape(buffer, "\e[11G", "buffer after tickit_term_goto col"); buffer[0] = 0; tickit_term_set_output_func(tt, output, buffer); tickit_term_print(tt, "Hello, world"); is_str(buffer, "Hello, world", "buffer after print using output func"); tickit_term_unref(tt); ok(1, "tickit_term_unref"); ok(output_eof, "output func receives EOF indication after tickit_term_unref"); return exit_status(); } libtickit-0.3.4/t/11term-output-screen.c0000644000000000000000000001143113613430267016162 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include #include #include #define RECT(t,l,li,co) (TickitRect){ .top = t, .left = l, .lines = li, .cols = co } void output(TickitTerm *tt, const char *bytes, size_t len, void *user) { char *buffer = user; strncat(buffer, bytes, len); } int main(int argc, char *argv[]) { TickitTerm *tt; char buffer[1024] = { 0 }; int lines, cols; tt = tickit_term_new_for_termtype("screen"); if(!tt && (errno == ENOENT || errno == EINVAL)) { /* Some systems (e.g. FreeBSD) provide extended terminfo cap strings for * screen's terminfo file, which current version of unibilium is unable to * parse. Just skip this test for now * See also * https://github.com/mauke/unibilium/pull/39 */ skip_all("unable to load screen terminfo"); return exit_status(); } ok(!!tt, "tickit_term_new_for_termtype"); if(!tt) { perror("tickit_term_new_for_termtype()"); exit(1); } is_str(tickit_term_get_termtype(tt), "screen", "tickit_term_get_termtype"); tickit_term_set_output_func(tt, output, buffer); is_int(tickit_term_get_output_fd(tt), -1, "tickit_term_get_output_fd"); tickit_term_set_size(tt, 24, 80); tickit_term_get_size(tt, &lines, &cols); is_int(lines, 24, "get_size lines"); is_int(cols, 80, "get_size cols"); buffer[0] = 0; tickit_term_print(tt, "Hello world!"); is_str_escape(buffer, "Hello world!", "buffer after tickit_term_print"); /* These tests rely on whatever is in the terminfo database, so we should * try not do anything too out of the ordinary */ buffer[0] = 0; tickit_term_goto(tt, 2, 5); is_str_escape(buffer, "\e[3;6H", "buffer after tickit_term_goto line+col"); buffer[0] = 0; tickit_term_goto(tt, -1, 0); is_str_escape(buffer, "\r", "buffer after tickit_term_goto col=0"); buffer[0] = 0; tickit_term_move(tt, 2, 0); is_str_escape(buffer, "\e[2B", "buffer after tickit_term_move down 2"); buffer[0] = 0; tickit_term_move(tt, -2, 0); is_str_escape(buffer, "\e[2A", "buffer after tickit_term_move down 2"); buffer[0] = 0; tickit_term_move(tt, 0, 2); is_str_escape(buffer, "\e[2C", "buffer after tickit_term_move right 2"); buffer[0] = 0; tickit_term_move(tt, 0, -2); is_str_escape(buffer, "\e[2D", "buffer after tickit_term_move left 2"); buffer[0] = 0; tickit_term_scrollrect(tt, RECT(3,0,7,80), 1, 0); is_str_escape(buffer, "\e[4;10r\e[4;1H\e[M\e[1;24r", "buffer after tickit_term_scrollrect lines 3-9 1 down"); buffer[0] = 0; tickit_term_scrollrect(tt, RECT(3,0,15,80), 8, 0); is_str_escape(buffer, "\e[4;18r\e[4;1H\e[8M\e[1;24r", "buffer after tickit_term_scrollrect lines 3-17 8 down"); buffer[0] = 0; tickit_term_scrollrect(tt, RECT(3,0,7,80), -1, 0); is_str_escape(buffer, "\e[4;10r\e[4;1H\e[L\e[1;24r", "buffer after tickit_term_scrollrect lines 3-9 1 up"); buffer[0] = 0; tickit_term_scrollrect(tt, RECT(3,0,15,80), -8, 0); is_str_escape(buffer, "\e[4;18r\e[4;1H\e[8L\e[1;24r", "buffer after tickit_term_scrollrect lines 3-17 8 up"); buffer[0] = 0; tickit_term_scrollrect(tt, RECT(5,0,1,80), 0, 3); is_str_escape(buffer, "\e[6;1H\e[3P", "buffer after tickit_term_scrollrect line 5 3 right"); buffer[0] = 0; tickit_term_scrollrect(tt, RECT(6,10,2,70), 0, 5); is_str_escape(buffer, "\e[7;11H\e[5P\e[8;11H\e[5P", "buffer after tickit_term_scrollrect lines 6-7 cols 10-80 5 right"); buffer[0] = 0; tickit_term_scrollrect(tt, RECT(5,0,1,80), 0, -3); is_str_escape(buffer, "\e[6;1H\e[3@", "buffer after tickit_term_scrollrect line 5 3 left"); buffer[0] = 0; tickit_term_scrollrect(tt, RECT(6,10,2,70), 0, -5); is_str_escape(buffer, "\e[7;11H\e[5@\e[8;11H\e[5@", "buffer after tickit_term_scrollrect lines 6-7 cols 10-80 5 left"); is_int(tickit_term_scrollrect(tt, RECT(3,10,5,60), 1, 0), 0, "tickit_term cannot scroll partial lines vertically"); is_int(tickit_term_scrollrect(tt, RECT(3,10,5,60), 0, 1), 0, "tickit_term cannot scroll partial lines horizontally"); buffer[0] = 0; tickit_term_clear(tt); is_str_escape(buffer, "\e[H\e[J", "buffer after tickit_term_clear"); buffer[0] = 0; tickit_term_erasech(tt, 1, 0); is_str_escape(buffer, " \b", "buffer after tickit_term_erasech 1 nomove"); buffer[0] = 0; tickit_term_erasech(tt, 3, 0); is_str_escape(buffer, " \e[3D", "buffer after tickit_term_erasech 3 nomove"); buffer[0] = 0; tickit_term_erasech(tt, 1, 1); is_str_escape(buffer, " ", "buffer after tickit_term_erasech 1 move"); buffer[0] = 0; tickit_term_erasech(tt, 3, 1); is_str_escape(buffer, " ", "buffer after tickit_term_erasech 3 move"); buffer[0] = 0; tickit_term_erasech(tt, 10, 1); is_str_escape(buffer, " ", "buffer after tickit_term_erasech 10 move"); tickit_term_unref(tt); pass("tickit_term_unref"); return exit_status(); } libtickit-0.3.4/t/11term-output-xterm.c0000644000000000000000000001342613613430267016050 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include #define RECT(t,l,li,co) (TickitRect){ .top = t, .left = l, .lines = li, .cols = co } void output(TickitTerm *tt, const char *bytes, size_t len, void *user) { char *buffer = user; strncat(buffer, bytes, len); } int main(int argc, char *argv[]) { TickitTerm *tt; char buffer[1024] = { 0 }; int lines, cols; tt = tickit_term_new_for_termtype("xterm"); ok(!!tt, "tickit_term_new_for_termtype"); is_str(tickit_term_get_termtype(tt), "xterm", "tickit_term_get_termtype"); tickit_term_set_output_func(tt, output, buffer); is_int(tickit_term_get_output_fd(tt), -1, "tickit_term_get_output_fd"); tickit_term_set_size(tt, 24, 80); tickit_term_get_size(tt, &lines, &cols); is_int(lines, 24, "get_size lines"); is_int(cols, 80, "get_size cols"); buffer[0] = 0; tickit_term_print(tt, "Hello world!"); is_str_escape(buffer, "Hello world!", "buffer after tickit_term_print"); buffer[0] = 0; tickit_term_goto(tt, 2, 5); is_str_escape(buffer, "\e[3;6H", "buffer after tickit_term_goto line+col"); buffer[0] = 0; tickit_term_goto(tt, 3, 0); is_str_escape(buffer, "\e[4H", "buffer after tickit_term_goto line+col0"); buffer[0] = 0; tickit_term_goto(tt, 4, -1); is_str_escape(buffer, "\e[5d", "buffer after tickit_term_goto line"); buffer[0] = 0; tickit_term_goto(tt, -1, 10); is_str_escape(buffer, "\e[11G", "buffer after tickit_term_goto col"); buffer[0] = 0; tickit_term_goto(tt, -1, 0); is_str_escape(buffer, "\e[G", "buffer after tickit_term_goto col0"); buffer[0] = 0; tickit_term_move(tt, 1, 0); is_str_escape(buffer, "\e[B", "buffer after tickit_term_move down 1"); buffer[0] = 0; tickit_term_move(tt, 2, 0); is_str_escape(buffer, "\e[2B", "buffer after tickit_term_move down 2"); buffer[0] = 0; tickit_term_move(tt, -1, 0); is_str_escape(buffer, "\e[A", "buffer after tickit_term_move down 1"); buffer[0] = 0; tickit_term_move(tt, -2, 0); is_str_escape(buffer, "\e[2A", "buffer after tickit_term_move down 2"); buffer[0] = 0; tickit_term_move(tt, 0, 1); is_str_escape(buffer, "\e[C", "buffer after tickit_term_move right 1"); buffer[0] = 0; tickit_term_move(tt, 0, 2); is_str_escape(buffer, "\e[2C", "buffer after tickit_term_move right 2"); buffer[0] = 0; tickit_term_move(tt, 0, -1); is_str_escape(buffer, "\e[D", "buffer after tickit_term_move left 1"); buffer[0] = 0; tickit_term_move(tt, 0, -2); is_str_escape(buffer, "\e[2D", "buffer after tickit_term_move left 2"); buffer[0] = 0; tickit_term_scrollrect(tt, RECT(3,0,7,80), 1, 0); is_str_escape(buffer, "\e[4;10r\e[4H\e[M\e[r", "buffer after tickit_term_scrollrect lines 3-9 1 down"); buffer[0] = 0; tickit_term_scrollrect(tt, RECT(3,0,15,80), 8, 0); is_str_escape(buffer, "\e[4;18r\e[4H\e[8M\e[r", "buffer after tickit_term_scrollrect lines 3-17 8 down"); buffer[0] = 0; tickit_term_scrollrect(tt, RECT(3,0,7,80), -1, 0); is_str_escape(buffer, "\e[4;10r\e[4H\e[L\e[r", "buffer after tickit_term_scrollrect lines 3-9 1 up"); buffer[0] = 0; tickit_term_scrollrect(tt, RECT(3,0,15,80), -8, 0); is_str_escape(buffer, "\e[4;18r\e[4H\e[8L\e[r", "buffer after tickit_term_scrollrect lines 3-17 8 up"); buffer[0] = 0; tickit_term_scrollrect(tt, RECT(5,0,1,80), 0, 3); is_str_escape(buffer, "\e[6H\e[3P", "buffer after tickit_term_scrollrect line 5 3 right"); buffer[0] = 0; tickit_term_scrollrect(tt, RECT(6,10,2,70), 0, 5); is_str_escape(buffer, "\e[7;11H\e[5P\e[8;11H\e[5P", "buffer after tickit_term_scrollrect lines 6-7 cols 10-80 5 right"); buffer[0] = 0; tickit_term_scrollrect(tt, RECT(5,0,1,80), 0, -3); is_str_escape(buffer, "\e[6H\e[3@", "buffer after tickit_term_scrollrect line 5 3 left"); buffer[0] = 0; tickit_term_scrollrect(tt, RECT(6,10,2,70), 0, -5); is_str_escape(buffer, "\e[7;11H\e[5@\e[8;11H\e[5@", "buffer after tickit_term_scrollrect lines 6-7 cols 10-80 5 left"); is_int(tickit_term_scrollrect(tt, RECT(3,10,5,60), 1, 0), 0, "tickit_term cannot scroll partial lines vertically"); is_int(tickit_term_scrollrect(tt, RECT(3,10,5,60), 0, 1), 0, "tickit_term cannot scroll partial lines horizontally"); /* Now (belatedly) respond to the DECSLRM probe to enable more scrollrect options */ tickit_term_input_push_bytes(tt, "\e[?69;1$y", 9); buffer[0] = 0; is_int(tickit_term_scrollrect(tt, RECT(3,10,5,60), 1, 0), 1, "tickit_term can scroll partial lines vertically with DECSLRM enabled"); is_str_escape(buffer, "\e[4;8r\e[11;70s\e[4;11H\e[M\e[r\e[s", "buffer after tickit_term_scroll lines 3-7 cols 10-69 down"); buffer[0] = 0; is_int(tickit_term_scrollrect(tt, RECT(3,10,5,60), 0, 1), 1, "tickit_term can scroll partial lines horizontally with DECSLRM enabled"); is_str_escape(buffer, "\e[4;8r\e[11;70s\e[4;11H\e['~\e[r\e[s", "buffer after tickit_term_scroll lines 3-7 cols 10-69 right"); buffer[0] = 0; is_int(tickit_term_scrollrect(tt, RECT(3,10,1,60), 0, 1), 1, "tickit_term can scroll partial lines horizontally with DECSLRM enabled"); is_str_escape(buffer, "\e[;70s\e[4;11H\e[P\e[s", "buffer after tickit_term_scroll line 3 cols 10-69 right"); buffer[0] = 0; tickit_term_clear(tt); is_str_escape(buffer, "\e[2J", "buffer after tickit_term_clear"); buffer[0] = 0; tickit_term_erasech(tt, 1, 0); is_str_escape(buffer, "\e[X", "buffer after tickit_term_erasech 1 nomove"); buffer[0] = 0; tickit_term_erasech(tt, 3, 0); is_str_escape(buffer, "\e[3X", "buffer after tickit_term_erasech 3 nomove"); buffer[0] = 0; tickit_term_erasech(tt, 1, 1); is_str_escape(buffer, "\e[X\e[C", "buffer after tickit_term_erasech 1 move"); buffer[0] = 0; tickit_term_erasech(tt, 3, 1); is_str_escape(buffer, "\e[3X\e[3C", "buffer after tickit_term_erasech 3 move"); tickit_term_unref(tt); pass("tickit_term_unref"); return exit_status(); } libtickit-0.3.4/t/12term-modes.c0000644000000000000000000000434713613430267014465 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include void output(TickitTerm *tt, const char *bytes, size_t len, void *user) { char *buffer = user; strncat(buffer, bytes, len); } int main(int argc, char *argv[]) { TickitTerm *tt; char buffer[1024] = { 0 }; int value; tt = tickit_term_new_for_termtype("xterm"); ok(!!tt, "tickit_term_new_for_termtype"); tickit_term_set_output_func(tt, output, buffer); is_str(tickit_term_ctlname(TICKIT_TERMCTL_ALTSCREEN), "altscreen", "tickit_term_ctlname on TICKIT_TERMCTL_ALTSCREEN"); is_int(tickit_term_lookup_ctl("cursorvis"), TICKIT_TERMCTL_CURSORVIS, "tickit_term_lookup_ctl on cursorvis"); buffer[0] = 0; tickit_term_setctl_int(tt, TICKIT_TERMCTL_ALTSCREEN, 1); is_str_escape(buffer, "\e[?1049h", "buffer after set_mode_altscreen on"); tickit_term_getctl_int(tt, TICKIT_TERMCTL_ALTSCREEN, &value); is_int(value, 1, "get_mode_altscreen returns value"); buffer[0] = 0; tickit_term_setctl_int(tt, TICKIT_TERMCTL_ALTSCREEN, 1); is_str_escape(buffer, "", "set_mode_altscreen a second time is idempotent"); buffer[0] = 0; tickit_term_setctl_int(tt, TICKIT_TERMCTL_CURSORVIS, 0); is_str_escape(buffer, "\e[?25l", "buffer after set_mode_cursorvis off"); buffer[0] = 0; tickit_term_setctl_int(tt, TICKIT_TERMCTL_MOUSE, TICKIT_TERM_MOUSEMODE_CLICK); is_str_escape(buffer, "\e[?1000h\e[?1006h", "buffer after set_mode_mouse to click"); buffer[0] = 0; tickit_term_setctl_int(tt, TICKIT_TERMCTL_MOUSE, TICKIT_TERM_MOUSEMODE_DRAG); is_str_escape(buffer, "\e[?1002h\e[?1006h", "buffer after set_mode_mouse to drag"); buffer[0] = 0; tickit_term_setctl_str(tt, TICKIT_TERMCTL_TITLE_TEXT, "title here"); is_str_escape(buffer, "\e]2;title here\e\\", "buffer after set title"); buffer[0] = 0; tickit_term_pause(tt); is_str_escape(buffer, "\e[?1002l\e[?1006l\e[?25h\e[?1049l\e[m", "buffer after pause"); buffer[0] = 0; tickit_term_resume(tt); is_str_escape(buffer, "\e[?1049h\e[?25l\e[?1002h\e[?1006h", "buffer after resume"); buffer[0] = 0; tickit_term_unref(tt); ok(1, "tickit_term_unref"); is_str_escape(buffer, "\e[?1002l\e[?1006l\e[?25h\e[?1049l\e[m", "buffer after termkey_term_unref resets modes"); return exit_status(); } libtickit-0.3.4/t/13term-pen.c0000644000000000000000000000572613613430267014143 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include void output(TickitTerm *tt, const char *bytes, size_t len, void *user) { char *buffer = user; strncat(buffer, bytes, len); } int main(int argc, char *argv[]) { TickitTerm *tt; char buffer[1024] = { 0 }; tt = tickit_term_new_for_termtype("xterm"); tickit_term_set_output_func(tt, output, buffer); // setpen empty { TickitPen *pen = tickit_pen_new(); buffer[0] = 0; tickit_term_setpen(tt, pen); is_str_escape(buffer, "\e[m", "buffer after setpen empty"); tickit_pen_unref(pen); } // adjust boolean attributes { TickitPen *pen = tickit_pen_new(); tickit_pen_set_bool_attr(pen, TICKIT_PEN_BOLD, 1); buffer[0] = 0; tickit_term_chpen(tt, pen); is_str_escape(buffer, "\e[1m", "buffer contains SGR 1 for chpen bold"); buffer[0] = 0; tickit_term_chpen(tt, pen); is_str_escape(buffer, "", "chpen again is a no-op"); tickit_pen_set_bool_attr(pen, TICKIT_PEN_BOLD, 0); buffer[0] = 0; tickit_term_chpen(tt, pen); is_str_escape(buffer, "\e[m", "chpen disables bold"); tickit_pen_set_bool_attr(pen, TICKIT_PEN_BOLD, 1); tickit_pen_set_bool_attr(pen, TICKIT_PEN_UNDER, 1); buffer[0] = 0; tickit_term_chpen(tt, pen); is_str_escape(buffer, "\e[1;4m", "chpen enables bold and under"); tickit_pen_set_bool_attr(pen, TICKIT_PEN_BOLD, 0); tickit_pen_clear_attr(pen, TICKIT_PEN_UNDER); buffer[0] = 0; tickit_term_chpen(tt, pen); is_str_escape(buffer, "\e[22m", "chpen disables bold"); buffer[0] = 0; tickit_term_chpen(tt, pen); is_str_escape(buffer, "", "chpen disable bold again is no-op"); tickit_pen_clear_attr(pen, TICKIT_PEN_BOLD); tickit_pen_set_bool_attr(pen, TICKIT_PEN_UNDER, 0); buffer[0] = 0; tickit_term_chpen(tt, pen); is_str_escape(buffer, "\e[m", "chpen disable under is reset"); tickit_pen_clear_attr(pen, TICKIT_PEN_UNDER); tickit_pen_unref(pen); } // adjust colours { TickitPen *pen = tickit_pen_new(); tickit_pen_set_colour_attr(pen, TICKIT_PEN_FG, 1); tickit_pen_set_colour_attr(pen, TICKIT_PEN_BG, 5); buffer[0] = 0; tickit_term_setpen(tt, pen); is_str_escape(buffer, "\e[31;45m", "chpen foreground+background"); tickit_pen_set_colour_attr(pen, TICKIT_PEN_FG, 9); tickit_pen_clear_attr(pen, TICKIT_PEN_BG); buffer[0] = 0; tickit_term_chpen(tt, pen); is_str_escape(buffer, "\e[91m", "chpen foreground high"); tickit_pen_set_colour_attr(pen, TICKIT_PEN_FG, 123); buffer[0] = 0; tickit_term_chpen(tt, pen); is_str_escape(buffer, "\e[38;5;123m", "chpen foreground xterm256"); tickit_pen_clear_attr(pen, TICKIT_PEN_FG); tickit_pen_set_bool_attr(pen, TICKIT_PEN_UNDER, 1); buffer[0] = 0; tickit_term_setpen(tt, pen); is_str_escape(buffer, "\e[39;49;4m", "setpen resets colours, enables under"); tickit_pen_unref(pen); } tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/14term-resize.c0000644000000000000000000000337213613430267014656 0ustar 00000000000000#include "tickit.h" #include "taplib.h" int new_lines, new_cols; int unbound = 0; int on_resize(TickitTerm *tt, TickitEventFlags flags, void *_info, void *data) { if(flags & TICKIT_EV_UNBIND) { unbound = 1; return 1; } TickitResizeEventInfo *info = _info; is_int(flags, TICKIT_EV_FIRE, "flags to on_resize()"); new_lines = info->lines; new_cols = info->cols; return 1; } int new_lines2, new_cols2; int on_resize2(TickitTerm *tt, TickitEventFlags flags, void *_info, void *data) { TickitResizeEventInfo *info = _info; is_int(flags, TICKIT_EV_FIRE, "flags to on_resize2()"); new_lines2 = info->lines; new_cols2 = info->cols; return 1; } int main(int argc, char *argv[]) { TickitTerm *tt; tt = tickit_term_new_for_termtype("xterm"); tickit_term_set_size(tt, 25, 80); int bindid = tickit_term_bind_event(tt, TICKIT_TERM_ON_RESIZE, TICKIT_BIND_UNBIND, on_resize, NULL); tickit_term_set_size(tt, 30, 100); is_int(new_lines, 30, "new_lines from event handler after set_size"); is_int(new_cols, 100, "new_cols from event handler after set_size"); tickit_term_bind_event(tt, TICKIT_TERM_ON_RESIZE, 0, on_resize2, NULL); tickit_term_set_size(tt, 35, 110); is_int(new_lines, 35, "new_lines from event handler after set_size"); is_int(new_cols, 110, "new_cols from event handler after set_size"); is_int(new_lines2, 35, "new_lines from event handler 2 after set_size"); is_int(new_cols2, 110, "new_cols from event handler 2 after set_size"); tickit_term_unbind_event_id(tt, bindid); tickit_term_set_size(tt, 40, 120); is_int(new_lines, 35, "new_lines still 35 after unbind event"); tickit_term_unref(tt); is_int(unbound, 1, "on_resize unbound after tickit_term_unref"); return exit_status(); } libtickit-0.3.4/t/15term-input.c0000644000000000000000000001643013613430267014514 0ustar 00000000000000#ifdef __GLIBC__ # define _XOPEN_SOURCE 500 // usleep #endif #include "tickit.h" #include "taplib.h" #include #include TickitKeyEventType keytype; char keystr[16]; int keymod; int on_key_return = 1; int on_key(TickitTerm *tt, TickitEventFlags flags, void *_info, void *data) { TickitKeyEventInfo *info = _info; keytype = info->type; strncpy(keystr, info->str, sizeof(keystr)-1); keystr[sizeof(keystr)-1] = 0; keymod = info->mod; return on_key_return; } int on_key_incr(TickitTerm *tt, TickitEventFlags flags, void *_info, void *data) { (*(int *)data)++; return 1; } TickitMouseEventType mousetype; int mousebutton, mouseline, mousecol; int mousemod; int on_mouse(TickitTerm *tt, TickitEventFlags flags, void *_info, void *data) { TickitMouseEventInfo *info = _info; mousetype = info->type; mousebutton = info->button; mouseline = info->line; mousecol = info->col; mousemod = info->mod; return 1; } char events[2]; uint8_t i = 0; int on_key_push(TickitTerm *tt, TickitEventFlags flags, void *info, void *user) { events[i++] = *(char*)user; return 0; } int main(int argc, char *argv[]) { TickitTerm *tt; tt = tickit_term_new_for_termtype("xterm"); tickit_term_set_utf8(tt, 1); ok(tickit_term_get_utf8(tt), "get_utf8 true"); tickit_term_bind_event(tt, TICKIT_TERM_ON_KEY, 0, on_key, NULL); tickit_term_bind_event(tt, TICKIT_TERM_ON_MOUSE, 0, on_mouse, NULL); { TickitKeyEventInfo info = { .type = TICKIT_KEYEV_TEXT, .mod = 0, .str = " ", }; tickit_term_emit_key(tt, &info); is_int(keytype, TICKIT_KEYEV_TEXT, "keytype after emit_key"); is_str(keystr, " ", "keystr after emit_key"); is_int(keymod, 0, "keymod after emit_key"); } tickit_term_input_push_bytes(tt, "A", 1); is_int(keytype, TICKIT_KEYEV_TEXT, "keytype after push_bytes A"); is_str(keystr, "A", "keystr after push_bytes A"); is_int(keymod, 0, "keymod after push_bytes A"); is_int(tickit_term_input_check_timeout_msec(tt), -1, "term has no timeout after A"); /* U+0109 - LATIN SMALL LETTER C WITH CIRCUMFLEX * UTF-8: 0xc4 0x89 */ tickit_term_input_push_bytes(tt, "\xc4\x89", 2); is_int(keytype, TICKIT_KEYEV_TEXT, "keytype after push_bytes U+0109"); is_str(keystr, "\xc4\x89", "keystr after push_bytes U+0109"); is_int(keymod, 0, "keymod after push_bytes U+0109"); tickit_term_input_push_bytes(tt, "\e[A", 3); is_int(keytype, TICKIT_KEYEV_KEY, "keytype after push_bytes Up"); is_str(keystr, "Up", "keystr after push_bytes Up"); is_int(keymod, 0, "keymod after push_bytes Up"); tickit_term_input_push_bytes(tt, "\x01", 1); is_int(keytype, TICKIT_KEYEV_KEY, "keytype after push_bytes C-a"); is_str(keystr, "C-a", "keystr after push_bytes C-a"); is_int(keymod, TICKIT_MOD_CTRL, "keymod after push_bytes C-a"); is_int(tickit_term_input_check_timeout_msec(tt), -1, "term has no timeout after Up"); { TickitMouseEventInfo info = { .type = TICKIT_MOUSEEV_PRESS, .button = 1, .line = 2, .col = 3, .mod = 0, }; tickit_term_emit_mouse(tt, &info); is_int(mousetype, TICKIT_MOUSEEV_PRESS, "mousetype after emit_mouse"); is_int(mousebutton, 1, "mousebutton after emit_mouse"); is_int(mouseline, 2, "mouseline after emit_mouse"); is_int(mousecol, 3, "mousecol after emit_mouse"); } tickit_term_input_push_bytes(tt, "\e[M !!", 6); is_int(mousetype, TICKIT_MOUSEEV_PRESS, "mousetype after mouse button press"); is_int(mousebutton, 1, "mousebutton after mouse button press"); is_int(mouseline, 0, "mouseline after mouse button press"); is_int(mousecol, 0, "mousecol after mouse button press"); is_int(mousemod, 0, "mousemod after mouse button press"); tickit_term_input_push_bytes(tt, "\e[M#!!", 6); is_int(mousetype, TICKIT_MOUSEEV_RELEASE, "mousetype after mouse button release"); is_int(mousebutton, 1, "mousebutton after mouse button release"); is_int(mouseline, 0, "mouseline after mouse button release"); is_int(mousecol, 0, "mousecol after mouse button release"); is_int(mousemod, 0, "mousemod after mouse button release"); tickit_term_input_push_bytes(tt, "\e[M`!!", 6); is_int(mousetype, TICKIT_MOUSEEV_WHEEL, "mousetype after mouse wheel up"); is_int(mousebutton, TICKIT_MOUSEWHEEL_UP, "mousebutton after mouse wheel up"); is_int(mouseline, 0, "mouseline after mouse wheel up"); is_int(mousecol, 0, "mousecol after mouse wheel up"); is_int(mousemod, 0, "mousemod after mouse wheel up"); keytype = -1; keystr[0] = 0; tickit_term_input_push_bytes(tt, "\e[", 2); is_int(keytype, -1, "keytype not set after push_bytes partial Down"); ok(tickit_term_input_check_timeout_msec(tt) > 0, "term has timeout after partial Down"); tickit_term_input_push_bytes(tt, "B", 1); is_int(keytype, TICKIT_KEYEV_KEY, "keytype after push_bytes completed Down"); is_str(keystr, "Down", "keystr after push_bytes completed Down"); is_int(tickit_term_input_check_timeout_msec(tt), -1, "term has no timeout after completed Down"); keytype = -1; keystr[0] = 0; tickit_term_input_push_bytes(tt, "\e", 1); is_int(keytype, -1, "keytype not set after push_bytes Escape"); int msec = tickit_term_input_check_timeout_msec(tt); ok(msec > 0, "term has timeout after Escape"); /* Add an extra milisecond timing grace */ usleep((msec+1) * 1000); tickit_term_input_check_timeout_msec(tt); is_int(keytype, TICKIT_KEYEV_KEY, "keytype after push_bytes completed Escape"); is_str(keystr, "Escape", "keystr after push_bytes completed Escape"); is_int(tickit_term_input_check_timeout_msec(tt), -1, "term has no timeout after completed Escape"); { int count = 0; int bind_id = tickit_term_bind_event(tt, TICKIT_TERM_ON_KEY, 0, &on_key_incr, &count); keytype = -1; on_key_return = 1; tickit_term_input_push_bytes(tt, "A", 1); is_int(keytype, TICKIT_KEYEV_TEXT, "keytype after push bytes with two events bound"); is_int(count, 0, "count of second event binding after first event with return 1"); keytype = -1; on_key_return = 0; tickit_term_input_push_bytes(tt, "A", 1); is_int(keytype, TICKIT_KEYEV_TEXT, "keytype after push bytes with two events bound the second time"); is_int(count, 1, "count of second event binding after first event with return 0"); tickit_term_unbind_event_id(tt, bind_id); } { int bindA_id = tickit_term_bind_event(tt, TICKIT_TERM_ON_KEY, 0, &on_key_push, "A"); int bindB_id = tickit_term_bind_event(tt, TICKIT_TERM_ON_KEY, TICKIT_BIND_FIRST, &on_key_push, "B"); tickit_term_input_push_bytes(tt, "X", 1); is_int(i, 2, "two events fired"); is_int(events[0], 'B', "TICKIT_BIND_FIRST event fired first"); tickit_term_unbind_event_id(tt, bindA_id); tickit_term_unbind_event_id(tt, bindB_id); } tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/16term-read.c0000644000000000000000000000340213613430267014264 0ustar 00000000000000#ifdef __GLIBC__ # define _XOPEN_SOURCE 500 // usleep #endif #include "tickit.h" #include "taplib.h" #include #include TickitKeyEventType keytype; char keystr[16]; int on_key(TickitTerm *tt, TickitEventFlags flags, void *_info, void *data) { TickitKeyEventInfo *info = _info; keytype = info->type; strncpy(keystr, info->str, sizeof(keystr)-1); keystr[sizeof(keystr)-1] = 0; return 1; } int main(int argc, char *argv[]) { TickitTerm *tt; int fd[2]; /* We'll need a real filehandle we can write/read. * pipe() can make us one */ pipe(fd); tt = tickit_term_new_for_termtype("xterm"); tickit_term_set_input_fd(tt, fd[0]); is_int(tickit_term_get_input_fd(tt), fd[0], "tickit_term_get_input_fd"); tickit_term_bind_event(tt, TICKIT_TERM_ON_KEY, 0, on_key, NULL); write(fd[1], "A", 1); tickit_term_input_readable(tt); is_int(keytype, TICKIT_KEYEV_TEXT, "keytype after write A"); is_str(keystr, "A", "keystr after write A"); is_int(tickit_term_input_check_timeout_msec(tt), -1, "term has no timeout after A"); keytype = -1; keystr[0] = 0; write(fd[1], "\e", 1); tickit_term_input_readable(tt); is_int(keytype, -1, "keytype not set after write Escape"); int msec = tickit_term_input_check_timeout_msec(tt); ok(msec > 0, "term has timeout after Escape"); /* Add an extra milisecond timing grace */ usleep((msec+1) * 1000); tickit_term_input_check_timeout_msec(tt); is_int(keytype, TICKIT_KEYEV_KEY, "keytype after write completed Escape"); is_str(keystr, "Escape", "keystr after write completed Escape"); is_int(tickit_term_input_check_timeout_msec(tt), -1, "term has no timeout after completed Escape"); tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/17term-buffer.c0000644000000000000000000000152213613430267014624 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include void output(TickitTerm *tt, const char *bytes, size_t len, void *user) { char *buffer = user; strncat(buffer, bytes, len); } int main(int argc, char *argv[]) { TickitTerm *tt; char buffer[1024] = { 0 }; tt = tickit_term_new_for_termtype("xterm"); ok(!!tt, "tickit_term_new_for_termtype"); tickit_term_set_output_func(tt, output, buffer); tickit_term_set_output_buffer(tt, 8); buffer[0] = 0; tickit_term_print(tt, "Hello "); is_str_escape(buffer, "", "buffer empty after print"); tickit_term_print(tt, "world!"); is_str_escape(buffer, "Hello wo", "buffer contains one spill after second print"); tickit_term_flush(tt); is_str_escape(buffer, "Hello world!", "buffer contains output after flush"); tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/18term-builder.c0000644000000000000000000000236013613430267015003 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include #define streq(a,b) (!strcmp(a,b)) static const char *ti_getstr(const char *name, const char *value, void *user) { if(streq(name, "cursor_address")) return "GOTO(%p1%d,%p2%d)"; return value; } static void output(TickitTerm *tt, const char *bytes, size_t len, void *user) { char *buffer = user; strncat(buffer, bytes, len); } int main(int argc, char *argv[]) { // termtype { struct TickitTermBuilder builder = { .termtype = "linux", }; TickitTerm *tt = tickit_term_build(&builder); is_str(tickit_term_get_termtype(tt), "linux", "termtype for built term"); tickit_term_unref(tt); } // getstr override { struct TickitTermBuilder builder = { .termtype = "linux", .ti_hook = &(const struct TickitTerminfoHook){ .getstr = ti_getstr }, }; TickitTerm *tt = tickit_term_build(&builder); char buffer[1024] = { 0 }; tickit_term_set_output_func(tt, output, buffer); tickit_term_set_output_buffer(tt, 4096); buffer[0] = 0; tickit_term_goto(tt, 4, 6); tickit_term_flush(tt); is_str_escape(buffer, "GOTO(4,6)", "buffer after goto for built term"); tickit_term_unref(tt); } return exit_status(); } libtickit-0.3.4/t/19term-driver.c0000644000000000000000000000356313613430267014657 0ustar 00000000000000#include "tickit.h" #include "tickit-termdrv.h" #include "taplib.h" #include static bool print(TickitTermDriver *ttd, const char *str, size_t len) { tickit_termdrv_write_strf(ttd, "PRINT(%.*s)", len, str); return true; } static bool getctl_int(TickitTermDriver *ttd, TickitTermCtl ctl, int *value) { switch(ctl) { case TICKIT_TERMCTL_COLORS: *value = 8; return true; default: break; } return false; } static bool setctl_int(TickitTermDriver *ttd, TickitTermCtl ctl, int value) { return false; } static bool setctl_str(TickitTermDriver *ttd, TickitTermCtl ctl, const char *value) { return false; } static TickitTermDriverVTable vtable = { .destroy = (void (*)(TickitTermDriver *))free, .print = print, /* Technically these are not optional but the test doesn't use them .goto_abs = goto_abs, .move_rel = move_rel, .scrollrect = scrollrect, .erasech = erasech, .clear = clear, .chpen = chpen, */ .getctl_int = getctl_int, .setctl_int = setctl_int, .setctl_str = setctl_str, }; void output(TickitTerm *tt, const char *bytes, size_t len, void *user) { char *buffer = user; strncat(buffer, bytes, len); } int on_key(TickitTerm *tt, TickitEventFlags type, void *_info, void *data) { TickitKeyEventInfo *info = _info; *((int *)data) = info->type; return 0; } int main(int argc, char *argv[]) { TickitTerm *tt; char buffer[1024] = { 0 }; TickitTermDriver *ttd = malloc(sizeof(TickitTermDriver)); ttd->vtable = &vtable; tt = tickit_term_new_for_driver(ttd); ok(!!tt, "tickit_term_new_for_driver"); tickit_term_set_output_func(tt, output, buffer); tickit_term_set_output_buffer(tt, 4096); buffer[0] = 0; tickit_term_print(tt, "Hello"); tickit_term_flush(tt); is_str(buffer, "PRINT(Hello)", "buffer after print"); tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/20mockterm.c0000644000000000000000000001445213613430267014227 0ustar 00000000000000#include "tickit.h" #include "tickit-mockterm.h" #include "taplib.h" #include "taplib-mockterm.h" #include #define streq(a,b) (!strcmp(a,b)) #define RECT(t,l,li,co) (TickitRect){ .top = t, .left = l, .lines = li, .cols = co } static TickitTerm *tt; static void is_display_text(char *name, ...) { va_list args; va_start(args, name); int lines, cols; tickit_term_get_size(tt, &lines, &cols); for(int line = 0; line < lines; line++) { const char *expect = va_arg(args, char *); size_t len = tickit_mockterm_get_display_text((TickitMockTerm *)tt, NULL, 0, line, 0, cols); char *got = malloc(len + 1); tickit_mockterm_get_display_text((TickitMockTerm *)tt, got, len, line, 0, cols); if(streq(expect, got)) { free(got); continue; } fail(name); diag("Got line % 2d |%s|", line, got); diag("Expected |%s|", expect); free(got); va_end(args); return; } va_end(args); pass(name); } static void fillterm(TickitTerm *tt) { tickit_term_goto(tt, 0, 0); tickit_term_print(tt, "0000000000"); tickit_term_goto(tt, 1, 0); tickit_term_print(tt, "1111111111"); tickit_term_goto(tt, 2, 0); tickit_term_print(tt, "2222222222"); } int on_key_event_get_type(TickitTerm *tt, TickitEventFlags flags, void *_info, void *data) { *((int *)data) = ((TickitKeyEventInfo *)_info)->type; return 0; } int on_mouse_event_get_type(TickitTerm *tt, TickitEventFlags flags, void *_info, void *data) { *((int *)data) = ((TickitMouseEventInfo *)_info)->type; return 0; } int main(int argc, char *argv[]) { tt = make_term(3, 10); is_termlog("Termlog initially", NULL); is_display_text("Display initially", " ", " ", " "); tickit_term_goto(tt, 1, 5); is_termlog("Termlog after goto", GOTO(1,5), NULL); tickit_term_print(tt, "foo"); is_termlog("Termlog after print", PRINT("foo"), NULL); is_display_text("Display after print", " ", " foo ", " "); char c = 'l'; tickit_term_printn(tt, &c, 1); is_termlog("Termlog after printn 1", PRINT("l"), NULL); is_display_text("Display after printn 1", " ", " fool ", " "); tickit_term_goto(tt, 2, 0); tickit_term_print(tt, "Ĉu vi?"); is_termlog("Termlog after print UTF-8", GOTO(2,0), PRINT("Ĉu vi?"), NULL); is_display_text("Display after print UTF-8", " ", " fool ", "Ĉu vi? "); // U+FF10 = Fullwidth digit zero = EF BC 90 tickit_term_print(tt, "\xef\xbc\x90"); is_termlog("Termlog after print UTF-8 fullwidth", PRINT("0"), NULL); is_display_text("Display after print UTF-8 fullwidth", " ", " fool ", "Ĉu vi?0 "); tickit_term_clear(tt); is_termlog("Termlog after clear", CLEAR(), NULL); is_display_text("Display after clear", " ", " ", " "); TickitPen *fg_pen = tickit_pen_new_attrs(TICKIT_PEN_FG, 3, 0); tickit_term_setpen(tt, fg_pen); is_termlog("Termlog after setpen", SETPEN(.fg=3), NULL); TickitPen *bg_pen = tickit_pen_new_attrs(TICKIT_PEN_BG, 6, 0); tickit_term_chpen(tt, bg_pen); is_termlog("Termlog after chpen", SETPEN(.fg=3, .bg=6), NULL); // Now some test content for scrolling fillterm(tt); tickit_mockterm_clearlog((TickitMockTerm *)tt); is_display_text("Display after scroll fill", "0000000000", "1111111111", "2222222222"); ok(tickit_term_scrollrect(tt, RECT(0,0,3,10), +1,0), "Scrollrect down OK"); is_termlog("Termlog after scroll 1 down", SCROLLRECT(0,0,3,10, +1,0), NULL); is_display_text("Display after scroll 1 down", "1111111111", "2222222222", " "); ok(tickit_term_scrollrect(tt, RECT(0,0,3,10), -1,0), "Scrollrect up OK"); is_termlog("Termlog after scroll 1 up", SCROLLRECT(0,0,3,10, -1,0), NULL); is_display_text("Display after scroll 1 up", " ", "1111111111", "2222222222"); fillterm(tt); tickit_mockterm_clearlog((TickitMockTerm *)tt); tickit_term_scrollrect(tt, RECT(0,0,2,10), +1,0); is_termlog("Termlog after scroll partial 1 down", SCROLLRECT(0,0,2,10, +1,0), NULL); is_display_text("Display after scroll partial 1 down", "1111111111", " ", "2222222222"); tickit_term_scrollrect(tt, RECT(0,0,2,10), -1,0); is_termlog("Termlog after scroll partial 1 up", SCROLLRECT(0,0,2,10, -1,0), NULL); is_display_text("Display after scroll partial 1 up", " ", "1111111111", "2222222222"); for(int line = 0; line < 3; line++) tickit_term_goto(tt, line, 0), tickit_term_print(tt, "ABCDEFGHIJ"); tickit_mockterm_clearlog((TickitMockTerm *)tt); tickit_term_scrollrect(tt, RECT(0,5,1,5), 0,2); is_termlog("Termlog after scroll right", SCROLLRECT(0,5,1,5, 0,+2), NULL); is_display_text("Display after scroll right", "ABCDEHIJ ", "ABCDEFGHIJ", "ABCDEFGHIJ"); tickit_term_scrollrect(tt, RECT(1,5,1,5), 0,-3); is_termlog("Termlog after scroll left", SCROLLRECT(1,5,1,5, 0,-3), NULL); is_display_text("Display after scroll left", "ABCDEHIJ ", "ABCDE FG", "ABCDEFGHIJ"); tickit_term_goto(tt, 2, 3); tickit_term_erasech(tt, 5, -1); is_termlog("Termlog after erasech", GOTO(2,3), ERASECH(5, -1), NULL); is_display_text("Display after erasech", "ABCDEHIJ ", "ABCDE FG", "ABC IJ"); { int type = 0; int key_bind_id = tickit_term_bind_event(tt, TICKIT_TERM_ON_KEY, 0, &on_key_event_get_type, &type); int mouse_bind_id = tickit_term_bind_event(tt, TICKIT_TERM_ON_MOUSE, 0, &on_mouse_event_get_type, &type); press_key(TICKIT_KEYEV_TEXT, "A", 0); is_int(type, TICKIT_KEYEV_TEXT, "type is TICKIT_KEYEV_TEXT after press_key()"); type = 0; press_mouse(TICKIT_MOUSEEV_PRESS, 1, 2, 3, 0); is_int(type, TICKIT_MOUSEEV_PRESS, "type is TICKIT_MOUSEEV_PRESS after press_mouse()"); tickit_term_unbind_event_id(tt, key_bind_id); tickit_term_unbind_event_id(tt, mouse_bind_id); } tickit_pen_unref(fg_pen); tickit_pen_unref(bg_pen); tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/30renderbuffer-span.c0000644000000000000000000002216513613430267016017 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include "taplib-mockterm.h" int main(int argc, char *argv[]) { TickitTerm *tt = make_term(25, 80); TickitRenderBuffer *rb; int lines, cols; char buffer[256]; rb = tickit_renderbuffer_new(10, 20); ok(!!rb, "tickit_renderbuffer_new"); tickit_renderbuffer_get_size(rb, &lines, &cols); is_int(lines, 10, "get_size lines"); is_int(cols, 20, "get_size cols"); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("Empty RenderBuffer renders nothing to term", NULL); ok(!tickit_renderbuffer_get_cell_active(rb, 0, 0), "get_cell_active SKIP"); // Absolute spans { TickitPen *fg_pen = tickit_pen_new_attrs(TICKIT_PEN_FG, 1, 0); tickit_renderbuffer_setpen(rb, fg_pen); cols = tickit_renderbuffer_text_at(rb, 0, 1, "text span"); is_int(cols, 9, "cols from text_at"); tickit_renderbuffer_erase_at(rb, 1, 1, 5); tickit_renderbuffer_textf_at(rb, 2, 1, "message %d", 123); is_int(tickit_renderbuffer_get_cell_text(rb, 0, 1, buffer, sizeof buffer), 1, "get_cell_text TEXT at 0,1"); is_str(buffer, "t", "buffer text at TEXT 0,1"); is_int(tickit_pen_get_colour_attr(tickit_renderbuffer_get_cell_pen(rb, 0, 1), TICKIT_PEN_FG), 1, "get_cell_pen FG at 0,1"); is_int(tickit_renderbuffer_get_cell_text(rb, 0, 2, buffer, sizeof buffer), 1, "get_cell_text TEXT at 0,2"); is_str(buffer, "e", "buffer text at TEXT 0,2"); is_int(tickit_renderbuffer_get_cell_text(rb, 1, 1, buffer, sizeof buffer), 0, "get_cell_text ERASE at 1,1"); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer renders text to terminal", GOTO(0,1), SETPEN(.fg=1), PRINT("text span"), GOTO(1,1), SETPEN(.fg=1), ERASECH(5,-1), GOTO(2,1), SETPEN(.fg=1), PRINT("message 123"), NULL); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer now empty after render to terminal", NULL); tickit_pen_unref(fg_pen); } // Formatting buffer edge case { TickitRenderBuffer *rb = tickit_renderbuffer_new(1, 80); is_int(tickit_renderbuffer_textf_at(rb, 0, 0, "%-64s", "ABC"), 64, "textf_at() returns 64 for boundary case"); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("Termlog after formatting via malloc'ed buffer", GOTO(0,0), SETPEN(), PRINT("ABC "), NULL); tickit_renderbuffer_unref(rb); } // UTF-8 handling { cols = tickit_renderbuffer_text_at(rb, 6, 0, "somé text ĉi tie"); is_int(cols, 16, "cols from text_at UTF-8"); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer renders UTF-8 text", GOTO(6,0), SETPEN(), PRINT("somé text ĉi tie"), NULL); } // Error conditions { is_int(tickit_renderbuffer_text_at(rb, 0, 0, "foo\nbar"), -1, "text_at() returns -1"); } // Span splitting { TickitPen *b_pen = tickit_pen_new_attrs(TICKIT_PEN_BOLD, 1, 0); // aaaAAaaa // BBBBBBBB // cccCCCCC // DDDDDddd tickit_renderbuffer_text_at(rb, 0, 0, "aaaaaaaa"); tickit_renderbuffer_text_at(rb, 1, 2, "bbbb"); tickit_renderbuffer_text_at(rb, 2, 0, "cccccc"); tickit_renderbuffer_text_at(rb, 3, 2, "dddddd"); { tickit_renderbuffer_savepen(rb); tickit_renderbuffer_setpen(rb, b_pen); tickit_renderbuffer_text_at(rb, 0, 3, "AA"); tickit_renderbuffer_text_at(rb, 1, 0, "BBBBBBBB"); tickit_renderbuffer_text_at(rb, 2, 3, "CCCCC"); tickit_renderbuffer_text_at(rb, 3, 0, "DDDDD"); tickit_renderbuffer_restore(rb); } // empty text should do nothing tickit_renderbuffer_text_at(rb, 4, 4, ""); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer spans can be split", GOTO(0,0), SETPEN(), PRINT("aaa"), SETPEN(.b=1), PRINT("AA"), SETPEN(), PRINT("aaa"), GOTO(1,0), SETPEN(.b=1), PRINT("BBBBBBBB"), GOTO(2,0), SETPEN(), PRINT("ccc"), SETPEN(.b=1), PRINT("CCCCC"), GOTO(3,0), SETPEN(.b=1), PRINT("DDDDD"), SETPEN(), PRINT("ddd"), NULL); tickit_pen_unref(b_pen); } { tickit_renderbuffer_text_at(rb, 0, 0, "abcdefghijkl"); tickit_renderbuffer_text_at(rb, 0, 2, "-"); tickit_renderbuffer_text_at(rb, 0, 4, "-"); tickit_renderbuffer_text_at(rb, 0, 6, "-"); tickit_renderbuffer_text_at(rb, 0, 8, "-"); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer renders overwritten text split chunks", GOTO(0,0), SETPEN(), PRINT("ab"), SETPEN(), PRINT("-"), // c SETPEN(), PRINT("d"), SETPEN(), PRINT("-"), // e SETPEN(), PRINT("f"), SETPEN(), PRINT("-"), // g SETPEN(), PRINT("h"), SETPEN(), PRINT("-"), // i SETPEN(), PRINT("jkl"), NULL); } // VC spans { TickitPen *fg_pen = tickit_pen_new_attrs(TICKIT_PEN_FG, 3, 0); tickit_renderbuffer_setpen(rb, fg_pen); tickit_renderbuffer_goto(rb, 0, 2); cols = tickit_renderbuffer_text(rb, "text span"); is_int(cols, 9, "cols from text"); tickit_renderbuffer_goto(rb, 1, 2); tickit_renderbuffer_erase(rb, 5); tickit_renderbuffer_goto(rb, 2, 2); tickit_renderbuffer_textf(rb, "another %08s", "string"); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer renders text at VC", GOTO(0,2), SETPEN(.fg=3), PRINT("text span"), GOTO(1,2), SETPEN(.fg=3), ERASECH(5,-1), GOTO(2,2), SETPEN(.fg=3), PRINT("another string"), NULL); tickit_pen_unref(fg_pen); } // Translation { tickit_renderbuffer_translate(rb, 3, 5); cols = tickit_renderbuffer_text_at(rb, 0, 0, "at 0,0"); is_int(cols, 6, "cols from text_at translated"); is_int(tickit_renderbuffer_get_cell_text(rb, 0, 0, buffer, sizeof buffer), 1, "get_cell_text TEXT at 0,0 translated"); is_str(buffer, "a", "buffer text at TEXT 0,0 translatd"); tickit_renderbuffer_goto(rb, 1, 0); int line, col; tickit_renderbuffer_get_cursorpos(rb, &line, &col); is_int(line, 1, "RenderBuffer line position after translate"); is_int(col, 0, "RenderBuffer column position after translate"); tickit_renderbuffer_text(rb, "at 1,0"); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer renders text with translation", GOTO(3,5), SETPEN(), PRINT("at 0,0"), GOTO(4,5), SETPEN(), PRINT("at 1,0"), NULL); } // Truncates correctly { cols = tickit_renderbuffer_textn_at(rb, 4, 0, "ABCDEFGHI", 3); is_int(cols, 3, "cols from textn_at truncated correctly"); cols = tickit_renderbuffer_textn_at(rb, 5, 1, "ABCDEF", 6); is_int(cols, 6, "cols from textn_at allows the full string"); cols = tickit_renderbuffer_textn_at(rb, 6, 2, "LMNOP", -1); is_int(cols, 5, "cols from textn_at handles -1"); tickit_renderbuffer_goto(rb, 7, 3); cols = tickit_renderbuffer_textn(rb, "QRSTUV", 4); is_int(cols, 4, "cols from textn truncated correctly"); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer textn rendering", GOTO(4,0), SETPEN(), PRINT("ABC"), GOTO(5,1), SETPEN(), PRINT("ABCDEF"), GOTO(6,2), SETPEN(), PRINT("LMNOP"), GOTO(7,3), SETPEN(), PRINT("QRST"), NULL); } // Eraserect { tickit_renderbuffer_eraserect(rb, &(TickitRect){.top = 2, .left = 3, .lines = 5, .cols = 8}); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer renders eraserect", GOTO(2,3), SETPEN(), ERASECH(8,-1), GOTO(3,3), SETPEN(), ERASECH(8,-1), GOTO(4,3), SETPEN(), ERASECH(8,-1), GOTO(5,3), SETPEN(), ERASECH(8,-1), GOTO(6,3), SETPEN(), ERASECH(8,-1), NULL); } // Skiprect { tickit_renderbuffer_eraserect(rb, &(TickitRect){.top = 0, .left = 0, .lines = 4, .cols = 10}); tickit_renderbuffer_skiprect(rb, &(TickitRect){.top = 1, .left = 2, .lines = 2, .cols = 6}); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer renders hole around skiprect", GOTO(0,0), SETPEN(), ERASECH(10,-1), GOTO(1,0), SETPEN(), ERASECH(2,-1), GOTO(1,8), SETPEN(), ERASECH(2,-1), GOTO(2,0), SETPEN(), ERASECH(2,-1), GOTO(2,8), SETPEN(), ERASECH(2,-1), GOTO(3,0), SETPEN(), ERASECH(10,-1), NULL); } // Clear { TickitPen *bg_pen = tickit_pen_new_attrs(TICKIT_PEN_BG, 3, 0); tickit_renderbuffer_setpen(rb, bg_pen); tickit_renderbuffer_clear(rb); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer renders clear", GOTO(0,0), SETPEN(.bg=3), ERASECH(20,-1), GOTO(1,0), SETPEN(.bg=3), ERASECH(20,-1), GOTO(2,0), SETPEN(.bg=3), ERASECH(20,-1), GOTO(3,0), SETPEN(.bg=3), ERASECH(20,-1), GOTO(4,0), SETPEN(.bg=3), ERASECH(20,-1), GOTO(5,0), SETPEN(.bg=3), ERASECH(20,-1), GOTO(6,0), SETPEN(.bg=3), ERASECH(20,-1), GOTO(7,0), SETPEN(.bg=3), ERASECH(20,-1), GOTO(8,0), SETPEN(.bg=3), ERASECH(20,-1), GOTO(9,0), SETPEN(.bg=3), ERASECH(20,-1), NULL); tickit_pen_unref(bg_pen); } tickit_renderbuffer_unref(rb); tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/31renderbuffer-line.c0000644000000000000000000000520613613430267016003 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include "taplib-mockterm.h" int main(int argc, char *argv[]) { TickitTerm *tt = make_term(25, 80); TickitRenderBuffer *rb; rb = tickit_renderbuffer_new(30, 30); // Simple lines, end caps { TickitPen *fg_pen = tickit_pen_new_attrs(TICKIT_PEN_FG, 1, 0); tickit_renderbuffer_setpen(rb, fg_pen); tickit_renderbuffer_hline_at(rb, 10, 10, 14, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_hline_at(rb, 11, 10, 14, TICKIT_LINE_SINGLE, TICKIT_LINECAP_START); tickit_renderbuffer_hline_at(rb, 12, 10, 14, TICKIT_LINE_SINGLE, TICKIT_LINECAP_END); tickit_renderbuffer_hline_at(rb, 13, 10, 14, TICKIT_LINE_SINGLE, TICKIT_LINECAP_BOTH); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer renders hline_at to terminal", GOTO(10,10), SETPEN(.fg=1), PRINT("╶───╴"), GOTO(11,10), SETPEN(.fg=1), PRINT("────╴"), GOTO(12,10), SETPEN(.fg=1), PRINT("╶────"), GOTO(13,10), SETPEN(.fg=1), PRINT("─────"), NULL); tickit_renderbuffer_setpen(rb, fg_pen); tickit_renderbuffer_vline_at(rb, 10, 13, 10, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_vline_at(rb, 10, 13, 11, TICKIT_LINE_SINGLE, TICKIT_LINECAP_START); tickit_renderbuffer_vline_at(rb, 10, 13, 12, TICKIT_LINE_SINGLE, TICKIT_LINECAP_END); tickit_renderbuffer_vline_at(rb, 10, 13, 13, TICKIT_LINE_SINGLE, TICKIT_LINECAP_BOTH); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer renders vline_at to terminal", GOTO(10,10), SETPEN(.fg=1), PRINT("╷│╷│"), GOTO(11,10), SETPEN(.fg=1), PRINT("││││"), GOTO(12,10), SETPEN(.fg=1), PRINT("││││"), GOTO(13,10), SETPEN(.fg=1), PRINT("╵╵││"), NULL); tickit_pen_unref(fg_pen); } // Line merging { tickit_renderbuffer_hline_at(rb, 10, 10, 14, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_hline_at(rb, 11, 10, 14, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_hline_at(rb, 12, 10, 14, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_vline_at(rb, 10, 12, 10, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_vline_at(rb, 10, 12, 12, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_vline_at(rb, 10, 12, 14, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer renders line merging", GOTO(10,10), SETPEN(), PRINT("┌─┬─┐"), GOTO(11,10), SETPEN(), PRINT("├─┼─┤"), GOTO(12,10), SETPEN(), PRINT("└─┴─┘"), NULL); } tickit_renderbuffer_unref(rb); tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/32renderbuffer-char.c0000644000000000000000000000351213613430267015770 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include "taplib-mockterm.h" int main(int argc, char *argv[]) { TickitTerm *tt = make_term(25, 80); TickitRenderBuffer *rb; char buffer[256]; rb = tickit_renderbuffer_new(10, 20); // Absolute characters { TickitPen *fg_pen = tickit_pen_new_attrs(TICKIT_PEN_FG, 4, 0); tickit_renderbuffer_setpen(rb, fg_pen); tickit_renderbuffer_char_at(rb, 5, 5, 0x41); tickit_renderbuffer_char_at(rb, 5, 6, 0x42); tickit_renderbuffer_char_at(rb, 5, 7, 0x43); is_int(tickit_renderbuffer_get_cell_text(rb, 5, 5, buffer, sizeof buffer), 1, "get_cell_text CHAR at 5,5"); is_str(buffer, "A", "buffer text at 5,5"); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer renders char_at to terminal", GOTO(5,5), SETPEN(.fg=4), PRINT("A"), SETPEN(.fg=4), PRINT("B"), SETPEN(.fg=4), PRINT("C"), NULL); tickit_pen_unref(fg_pen); } // VC characters { tickit_renderbuffer_goto(rb, 0, 4); TickitPen *fg_pen = tickit_pen_new_attrs(TICKIT_PEN_FG, 5, 0); tickit_renderbuffer_setpen(rb, fg_pen); tickit_renderbuffer_char(rb, 0x47); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer renders chars at VC", GOTO(0,4), SETPEN(.fg=5), PRINT("G"), NULL); tickit_pen_unref(fg_pen); } // Characters with translation { tickit_renderbuffer_translate(rb, 3, 5); tickit_renderbuffer_char_at(rb, 1, 1, 0x31); tickit_renderbuffer_char_at(rb, 1, 2, 0x32); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer renders char_at with translation", GOTO(4,6), SETPEN(), PRINT("1"), SETPEN(), PRINT("2"), NULL); } tickit_renderbuffer_unref(rb); tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/33renderbuffer-clip.c0000644000000000000000000001137713613430267016013 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include "taplib-mockterm.h" int main(int argc, char *argv[]) { TickitTerm *tt = make_term(25, 80); TickitRenderBuffer *rb; int len; rb = tickit_renderbuffer_new(10, 20); // Clipping to edge { len = tickit_renderbuffer_text_at(rb, -1, 5, "TTTTTTTTTT"); is_int(len, 10, "len from text_at clipped off top"); len = tickit_renderbuffer_text_at(rb, 11, 5, "BBBBBBBBBB"); is_int(len, 10, "len from text_at clipped off bottom"); len = tickit_renderbuffer_text_at(rb, 4, -3, "[LLLLLLLL]"); is_int(len, 10, "len from text_at clipped off left"); len = tickit_renderbuffer_text_at(rb, 5, 15, "[RRRRRRRR]"); is_int(len, 10, "len from text_at clipped off right"); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer text rendering with clipping", GOTO(4,0), SETPEN(), PRINT("LLLLLL]"), GOTO(5,15), SETPEN(), PRINT("[RRRR"), NULL); { tickit_renderbuffer_savepen(rb); TickitPen *pen = tickit_pen_new(); tickit_pen_set_colour_attr(pen, TICKIT_PEN_FG, 1); tickit_renderbuffer_setpen(rb, pen); tickit_renderbuffer_erase_at(rb, -1, 5, 10); tickit_pen_set_colour_attr(pen, TICKIT_PEN_FG, 2); tickit_renderbuffer_setpen(rb, pen); tickit_renderbuffer_erase_at(rb, 11, 5, 10); tickit_pen_set_colour_attr(pen, TICKIT_PEN_FG, 3); tickit_renderbuffer_setpen(rb, pen); tickit_renderbuffer_erase_at(rb, 4, -3, 10); tickit_pen_set_colour_attr(pen, TICKIT_PEN_FG, 4); tickit_renderbuffer_setpen(rb, pen); tickit_renderbuffer_erase_at(rb, 5, 15, 10); tickit_pen_unref(pen); tickit_renderbuffer_restore(rb); } tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer erasech rendering with clipping", GOTO(4,0), SETPEN(.fg=3), ERASECH(7,-1), GOTO(5,15), SETPEN(.fg=4), ERASECH(5,-1), NULL); tickit_renderbuffer_goto(rb, 2, 18); tickit_renderbuffer_text(rb, "A"); tickit_renderbuffer_text(rb, "B"); tickit_renderbuffer_text(rb, "C"); tickit_renderbuffer_text(rb, "D"); tickit_renderbuffer_text(rb, "E"); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer text at VC with clipping", GOTO(2,18), SETPEN(), PRINT("A"), SETPEN(), PRINT("B"), NULL); } // Clipping to rect { tickit_renderbuffer_clip(rb, &(TickitRect){.top = 2, .left=2, .lines=6, .cols=16}); tickit_renderbuffer_text_at(rb, 1, 5, "TTTTTTTTTT"); tickit_renderbuffer_text_at(rb, 9, 5, "BBBBBBBBBB"); tickit_renderbuffer_text_at(rb, 4, -3, "[LLLLLLLL]"); tickit_renderbuffer_text_at(rb, 5, 15, "[RRRRRRRR]"); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer renders text rendering with rect clipping", GOTO(4,2), SETPEN(), PRINT("LLLL]"), GOTO(5,15), SETPEN(), PRINT("[RR"), NULL); tickit_renderbuffer_clip(rb, &(TickitRect){.top = 2, .left=2, .lines=6, .cols=16}); { tickit_renderbuffer_savepen(rb); TickitPen *pen = tickit_pen_new(); tickit_pen_set_colour_attr(pen, TICKIT_PEN_FG, 1); tickit_renderbuffer_setpen(rb, pen); tickit_renderbuffer_erase_at(rb, 1, 5, 10); tickit_pen_set_colour_attr(pen, TICKIT_PEN_FG, 2); tickit_renderbuffer_setpen(rb, pen); tickit_renderbuffer_erase_at(rb, 9, 5, 10); tickit_pen_set_colour_attr(pen, TICKIT_PEN_FG, 3); tickit_renderbuffer_setpen(rb, pen); tickit_renderbuffer_erase_at(rb, 4, -3, 10); tickit_pen_set_colour_attr(pen, TICKIT_PEN_FG, 4); tickit_renderbuffer_setpen(rb, pen); tickit_renderbuffer_erase_at(rb, 5, 15, 10); tickit_pen_unref(pen); tickit_renderbuffer_restore(rb); } tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer renders erasech with rect clipping", GOTO(4,2), SETPEN(.fg=3), ERASECH(5,-1), GOTO(5,15), SETPEN(.fg=4), ERASECH(3,-1), NULL); } // Clipping with translation { tickit_renderbuffer_translate(rb, 3, 5); tickit_renderbuffer_clip(rb, &(TickitRect){.top = 2, .left = 2, .lines = 3, .cols = 5}); tickit_renderbuffer_text_at(rb, 1, 0, "1111111111"); tickit_renderbuffer_text_at(rb, 2, 0, "2222222222"); tickit_renderbuffer_text_at(rb, 3, 0, "3333333333"); tickit_renderbuffer_text_at(rb, 4, 0, "4444444444"); tickit_renderbuffer_text_at(rb, 5, 0, "5555555555"); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer clipping rectangle translated", GOTO(5,7), SETPEN(), PRINT("22222"), GOTO(6,7), SETPEN(), PRINT("33333"), GOTO(7,7), SETPEN(), PRINT("44444"), NULL); } tickit_renderbuffer_unref(rb); tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/34renderbuffer-save.c0000644000000000000000000000763013613430267016020 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include "taplib-mockterm.h" int main(int argc, char *argv[]) { TickitTerm *tt = make_term(25, 80); TickitRenderBuffer *rb; rb = tickit_renderbuffer_new(10, 20); // Position { int line, col; tickit_renderbuffer_goto(rb, 2, 2); { tickit_renderbuffer_save(rb); tickit_renderbuffer_goto(rb, 4, 4); tickit_renderbuffer_get_cursorpos(rb, &line, &col); is_int(line, 4, "line before restore"); is_int(col, 4, "col before restore"); tickit_renderbuffer_restore(rb); } tickit_renderbuffer_get_cursorpos(rb, &line, &col); is_int(line, 2, "line after restore"); is_int(col, 2, "col after restore"); tickit_renderbuffer_text(rb, "some text"); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("Stack saves/restores virtual cursor position", GOTO(2,2), SETPEN(), PRINT("some text"), NULL); } // Clipping { tickit_renderbuffer_text_at(rb, 0, 0, "0000000000"); { tickit_renderbuffer_save(rb); tickit_renderbuffer_clip(rb, &(TickitRect){.top = 0, .left = 2, .lines = 10, .cols = 16}); tickit_renderbuffer_text_at(rb, 1, 0, "1111111111"); tickit_renderbuffer_restore(rb); } tickit_renderbuffer_text_at(rb, 2, 0, "2222222222"); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("Stack saves/restores clipping region", GOTO(0,0), SETPEN(), PRINT("0000000000"), GOTO(1,2), SETPEN(), PRINT("11111111"), GOTO(2,0), SETPEN(), PRINT("2222222222"), NULL); } // Pen { TickitPen *pen; tickit_renderbuffer_goto(rb, 3, 0); tickit_renderbuffer_setpen(rb, pen = tickit_pen_new_attrs(TICKIT_PEN_BG, 1, 0)); tickit_pen_unref(pen); tickit_renderbuffer_text(rb, "123"); { tickit_renderbuffer_savepen(rb); tickit_renderbuffer_setpen(rb, pen = tickit_pen_new_attrs(TICKIT_PEN_FG, 4, 0)); tickit_pen_unref(pen); tickit_renderbuffer_text(rb, "456"); tickit_renderbuffer_setpen(rb, pen = tickit_pen_new_attrs(TICKIT_PEN_BG, -1, 0)); tickit_pen_unref(pen); tickit_renderbuffer_text(rb, "789"); tickit_renderbuffer_restore(rb); } tickit_renderbuffer_text(rb, "ABC"); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("Stack saves/restores render pen", GOTO(3,0), SETPEN(.bg=1), PRINT("123"), SETPEN(.bg=1,.fg=4), PRINT("456"), SETPEN(), PRINT("789"), SETPEN(.bg=1), PRINT("ABC"), NULL); tickit_renderbuffer_goto(rb, 4, 0); tickit_renderbuffer_setpen(rb, pen = tickit_pen_new_attrs(TICKIT_PEN_REVERSE, 1, 0)); tickit_pen_unref(pen); tickit_renderbuffer_text(rb, "123"); { tickit_renderbuffer_savepen(rb); tickit_renderbuffer_setpen(rb, pen = tickit_pen_new_attrs(TICKIT_PEN_REVERSE, 0, 0)); tickit_pen_unref(pen); tickit_renderbuffer_text(rb, "456"); tickit_renderbuffer_restore(rb); } tickit_renderbuffer_text(rb, "789"); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("Stack saves/restores allow zeroing pen attributes", GOTO(4,0), SETPEN(.rv=1), PRINT("123"), SETPEN(), PRINT("456"), SETPEN(.rv=1), PRINT("789"), NULL); } // Translation { tickit_renderbuffer_text_at(rb, 0, 0, "A"); tickit_renderbuffer_save(rb); { tickit_renderbuffer_translate(rb, 2, 2); tickit_renderbuffer_text_at(rb, 1, 1, "B"); } tickit_renderbuffer_restore(rb); tickit_renderbuffer_text_at(rb, 2, 2, "C"); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("Stack saves/restores translation offset", GOTO(0,0), SETPEN(), PRINT("A"), GOTO(2,2), SETPEN(), PRINT("C"), GOTO(3,3), SETPEN(), PRINT("B"), NULL); } tickit_renderbuffer_unref(rb); tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/35renderbuffer-mask.c0000644000000000000000000001031413613430267016007 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include "taplib-mockterm.h" int main(int argc, char *argv[]) { TickitTerm *tt = make_term(25, 80); TickitRenderBuffer *rb; rb = tickit_renderbuffer_new(10, 20); TickitRect mask = {.top = 3, .left = 5, .lines = 4, .cols = 6}; // Mask over text { tickit_renderbuffer_mask(rb, &mask); tickit_renderbuffer_text_at(rb, 3, 2, "ABCDEFG"); // before tickit_renderbuffer_text_at(rb, 4, 6, "HI"); // inside tickit_renderbuffer_text_at(rb, 5, 8, "JKLMN"); // after tickit_renderbuffer_text_at(rb, 6, 2, "OPQRSTUVWXYZ"); // spanning tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer masking around text", GOTO(3, 2), SETPEN(), PRINT("ABC"), GOTO(5,11), SETPEN(), PRINT("MN"), GOTO(6, 2), SETPEN(), PRINT("OPQ"), GOTO(6,11), SETPEN(), PRINT("XYZ"), NULL); } // Mask over erase { tickit_renderbuffer_mask(rb, &mask); tickit_renderbuffer_erase_at(rb, 3, 2, 6); // before tickit_renderbuffer_erase_at(rb, 4, 6, 2); // inside tickit_renderbuffer_erase_at(rb, 5, 8, 5); // after tickit_renderbuffer_erase_at(rb, 6, 2, 12); // spanning tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer masking around erasech", GOTO(3, 2), SETPEN(), ERASECH(3,-1), GOTO(5,11), SETPEN(), ERASECH(2,-1), GOTO(6, 2), SETPEN(), ERASECH(3,-1), GOTO(6,11), SETPEN(), ERASECH(3,-1), NULL); } // Mask over lines { tickit_renderbuffer_mask(rb, &mask); tickit_renderbuffer_hline_at(rb, 3, 2, 8, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_hline_at(rb, 4, 6, 8, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_hline_at(rb, 5, 8, 13, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_hline_at(rb, 6, 2, 14, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer masking around lines", GOTO(3, 2), SETPEN(), PRINT("╶──"), GOTO(5,11), SETPEN(), PRINT("──╴"), GOTO(6, 2), SETPEN(), PRINT("╶──"), GOTO(6,11), SETPEN(), PRINT("───╴"), NULL); } // Restore removes masks { tickit_renderbuffer_save(rb); { tickit_renderbuffer_mask(rb, &mask); tickit_renderbuffer_text_at(rb, 3, 0, "AAAAAAAAAAAAAAAAAAAA"); } tickit_renderbuffer_restore(rb); tickit_renderbuffer_text_at(rb, 4, 0, "BBBBBBBBBBBBBBBBBBBB"); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer save/restore removes mask", GOTO(3, 0), SETPEN(), PRINT("AAAAA"), GOTO(3,11), SETPEN(), PRINT("AAAAAAAAA"), GOTO(4, 0), SETPEN(), PRINT("BBBBBBBBBBBBBBBBBBBB"), NULL); } // translate over mask { tickit_renderbuffer_mask(rb, &(TickitRect){.top = 2, .left = 2, .lines = 1, .cols = 1}); { tickit_renderbuffer_save(rb); tickit_renderbuffer_translate(rb, 0, 0); tickit_renderbuffer_text_at(rb, 0, 0, "A"); tickit_renderbuffer_restore(rb); } { tickit_renderbuffer_save(rb); tickit_renderbuffer_translate(rb, 0, 2); tickit_renderbuffer_text_at(rb, 0, 0, "B"); tickit_renderbuffer_restore(rb); } { tickit_renderbuffer_save(rb); tickit_renderbuffer_translate(rb, 2, 0); tickit_renderbuffer_text_at(rb, 0, 0, "C"); tickit_renderbuffer_restore(rb); } { tickit_renderbuffer_save(rb); tickit_renderbuffer_translate(rb, 2, 2); tickit_renderbuffer_text_at(rb, 0, 0, "D"); tickit_renderbuffer_restore(rb); } tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer translate over mask", GOTO(0,0), SETPEN(), PRINT("A"), GOTO(0,2), SETPEN(), PRINT("B"), GOTO(2,0), SETPEN(), PRINT("C"), // D was masked NULL); } // Mask out of limits doesn't SEGV { tickit_renderbuffer_save(rb); tickit_renderbuffer_mask(rb, &(TickitRect){.top = 0, .left = 0, .lines = 50, .cols = 200}); tickit_renderbuffer_mask(rb, &(TickitRect){.top = -10, .left = -20, .lines = 5, .cols = 20}); tickit_renderbuffer_restore(rb); } tickit_renderbuffer_unref(rb); tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/36renderbuffer-blit.c0000644000000000000000000001240513613430267016012 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include "taplib-mockterm.h" int main(int argc, char *argv[]) { TickitTerm *tt = make_term(25, 80); TickitRenderBuffer *screen, *window; screen = tickit_renderbuffer_new(25, 80); window = tickit_renderbuffer_new(10, 20); // Basic blit { tickit_renderbuffer_text_at(window, 0, 0, "Hello"); tickit_renderbuffer_char_at(window, 1, 1, 'A'); tickit_renderbuffer_skip_at(window, 2, 2, 2); tickit_renderbuffer_blit(screen, window); tickit_renderbuffer_flush_to_term(screen, tt); is_termlog("RenderBuffer basic blitting", GOTO(0,0), SETPEN(), PRINT("Hello"), GOTO(1,1), SETPEN(), PRINT("A"), NULL); tickit_renderbuffer_blit(screen, window); tickit_renderbuffer_flush_to_term(screen, tt); is_termlog("RenderBuffer blitting doesn't wipe src rb", GOTO(0,0), SETPEN(), PRINT("Hello"), GOTO(1,1), SETPEN(), PRINT("A"), NULL); } // Blitting an erase wipes underlying content { tickit_renderbuffer_reset(window); tickit_renderbuffer_text_at(screen, 0, 0, "Hello"); tickit_renderbuffer_erase_at(window, 0, 0, 4); tickit_renderbuffer_blit(screen, window); tickit_renderbuffer_flush_to_term(screen, tt); is_termlog("RenderBuffer blit can erase underlying content", GOTO(0,0), SETPEN(), ERASECH(4,1), SETPEN(), PRINT("o"), NULL); } // Blitting text that spans a mask { tickit_renderbuffer_reset(window); TickitRect mask = { .top = 0, .left = 2, .lines = 1, .cols = 1 }; tickit_renderbuffer_mask(window, &mask); tickit_renderbuffer_text_at(window, 0, 0, "Hello"); tickit_renderbuffer_blit(screen, window); tickit_renderbuffer_flush_to_term(screen, tt); is_termlog("RenderBuffer blit can have text spanning a mask", GOTO(0,0), SETPEN(), PRINT("He"), GOTO(0,3), SETPEN(), PRINT("lo"), NULL); } // Blitting merges line segments { tickit_renderbuffer_reset(window); tickit_renderbuffer_hline_at(window, 1, 0, 20, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_vline_at(screen, 0, 2, 5, TICKIT_LINE_SINGLE, 0); tickit_renderbuffer_blit(screen, window); tickit_renderbuffer_flush_to_term(screen, tt); is_termlog("RenderBuffer blit merges line segments", GOTO(0,5), SETPEN(), PRINT("╷"), GOTO(1,0), SETPEN(), PRINT("╶────┼──────────────"), GOTO(2,5), SETPEN(), PRINT("╵"), NULL); } // Blitting obeys translation { tickit_renderbuffer_reset(window); tickit_renderbuffer_translate(window, 3, 3); tickit_renderbuffer_translate(screen, 2, 4); tickit_renderbuffer_text_at(window, 0, 1, "Hello"); tickit_renderbuffer_char_at(window, 2, 4, 'B'); tickit_renderbuffer_blit(screen, window); tickit_renderbuffer_flush_to_term(screen, tt); is_termlog("RenderBuffer blit obeys translation", GOTO(5,8), SETPEN(), PRINT("Hello"), GOTO(7,11), SETPEN(), PRINT("B"), NULL); } // Blitting obeys clipping { tickit_renderbuffer_reset(window); TickitRect clip = { .top=2, .left=2, .lines=6, .cols=16 }; tickit_renderbuffer_clip(screen, &clip); tickit_renderbuffer_text_at(window, 1, 0, "Hello"); tickit_renderbuffer_text_at(window, 2, 0, "World"); tickit_renderbuffer_char_at(window, 2, 19, 'C'); tickit_renderbuffer_blit(screen, window); tickit_renderbuffer_flush_to_term(screen, tt); is_termlog("RenderBuffer blit obeys clipping", GOTO(2,2), SETPEN(), PRINT("rld"), NULL); } // Blitting overrides destination's pen { tickit_renderbuffer_reset(window); TickitPen *screen_pen = tickit_pen_new_attrs( TICKIT_PEN_FG, 4, 0); tickit_renderbuffer_setpen(screen, screen_pen); tickit_pen_unref(screen_pen); TickitPen *window_pen = tickit_pen_new_attrs( TICKIT_PEN_FG, 5, 0); tickit_renderbuffer_setpen(window, window_pen); tickit_pen_unref(window_pen); TickitPen *bg_pen = tickit_pen_new_attrs( TICKIT_PEN_BG, 6, 0); tickit_renderbuffer_text_at(window, 0, 0, "Hello"); { tickit_renderbuffer_savepen(window); tickit_renderbuffer_setpen(window, bg_pen); tickit_renderbuffer_char_at(window, 1, 1, 'A'); tickit_renderbuffer_restore(window); } tickit_renderbuffer_setpen(window, NULL); { tickit_renderbuffer_savepen(window); tickit_renderbuffer_setpen(window, bg_pen); tickit_renderbuffer_text_at(window, 2, 2, "World"); tickit_renderbuffer_restore(window); } tickit_renderbuffer_text_at(window, 3, 3, "Again"); tickit_renderbuffer_text_at(screen, 4, 4, "Preserved Pen"); tickit_renderbuffer_blit(screen, window); tickit_renderbuffer_flush_to_term(screen, tt); is_termlog("RenderBuffer basic blitting", GOTO(0,0), SETPEN(.fg=5), PRINT("Hello"), GOTO(1,1), SETPEN(.fg=5,.bg=6), PRINT("A"), GOTO(2,2), SETPEN(.fg=4,.bg=6), PRINT("World"), GOTO(3,3), SETPEN(.fg=4), PRINT("Again"), GOTO(4,4), SETPEN(.fg=4), PRINT("Preserved Pen"), NULL); tickit_pen_unref(bg_pen); } tickit_renderbuffer_unref(window); tickit_renderbuffer_unref(screen); tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/37renderbuffer-copy.c0000644000000000000000000001334313613430267016035 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include "taplib-mockterm.h" int main(int argc, char *argv[]) { TickitTerm *tt = make_term(25, 80); TickitRenderBuffer *rb; rb = tickit_renderbuffer_new(10, 20); // Basic copy { tickit_renderbuffer_text_at(rb, 0, 0, "Hello"); tickit_renderbuffer_char_at(rb, 1, 1, 'A'); tickit_renderbuffer_erase_at(rb, 1, 2, 3); tickit_renderbuffer_copyrect(rb, &(TickitRect){ .top = 4, .left = 2, .lines = 2, .cols = 10 }, &(TickitRect){ .top = 0, .left = 0, .lines = 2, .cols = 10 }); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer contents duplicated after copyrect", /* orig */ GOTO(0,0), SETPEN(), PRINT("Hello"), GOTO(1,1), SETPEN(), PRINT("A"), SETPEN(), ERASECH(3,-1), /* copy */ GOTO(4,2), SETPEN(), PRINT("Hello"), GOTO(5,3), SETPEN(), PRINT("A"), SETPEN(), ERASECH(3,-1), NULL); } // Truncate right { tickit_renderbuffer_text_at(rb, 0, 0, "ABCDE"); tickit_renderbuffer_erase_at(rb, 1, 0, 6); tickit_renderbuffer_copyrect(rb, &(TickitRect){ .top = 4, .left = 0, .lines = 2, .cols = 3 }, &(TickitRect){ .top = 0, .left = 0, .lines = 2, .cols = 3 }); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer copyrect can truncate to right", /* orig */ GOTO(0,0), SETPEN(), PRINT("ABCDE"), GOTO(1,0), SETPEN(), ERASECH(6,-1), /* copy */ GOTO(4,0), SETPEN(), PRINT("ABC"), GOTO(5,0), SETPEN(), ERASECH(3,-1), NULL); } // Truncate left { tickit_renderbuffer_text_at(rb, 0, 0, "ABCDE"); tickit_renderbuffer_erase_at(rb, 1, 0, 6); tickit_renderbuffer_copyrect(rb, &(TickitRect){ .top = 4, .left = 2, .lines = 2, .cols = 3 }, &(TickitRect){ .top = 0, .left = 2, .lines = 2, .cols = 3 }); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer copyrect can truncate to left", /* orig */ GOTO(0,0), SETPEN(), PRINT("ABCDE"), GOTO(1,0), SETPEN(), ERASECH(6,-1), /* copy */ GOTO(4,2), SETPEN(), PRINT("CDE"), GOTO(5,2), SETPEN(), ERASECH(3,-1), NULL); } // Overlap upwards { tickit_renderbuffer_text_at(rb, 0, 0, "Abcde"); tickit_renderbuffer_text_at(rb, 1, 0, "Fghij"); tickit_renderbuffer_text_at(rb, 2, 0, "Klmno"); tickit_renderbuffer_copyrect(rb, &(TickitRect){ .top = 0, .left = 0, .lines = 2, .cols = 10 }, &(TickitRect){ .top = 1, .left = 0, .lines = 2, .cols = 10 }); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer copyrect can copy upwards", GOTO(0,0), SETPEN(), PRINT("Fghij"), GOTO(1,0), SETPEN(), PRINT("Klmno"), GOTO(2,0), SETPEN(), PRINT("Klmno"), NULL); } // Overlap downwards { tickit_renderbuffer_text_at(rb, 0, 0, "aBcde"); tickit_renderbuffer_text_at(rb, 1, 0, "fGhij"); tickit_renderbuffer_text_at(rb, 2, 0, "kLmno"); tickit_renderbuffer_copyrect(rb, &(TickitRect){ .top = 1, .left = 0, .lines = 2, .cols = 10 }, &(TickitRect){ .top = 0, .left = 0, .lines = 2, .cols = 10 }); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer copyrect can copy downwards", GOTO(0,0), SETPEN(), PRINT("aBcde"), GOTO(1,0), SETPEN(), PRINT("aBcde"), GOTO(2,0), SETPEN(), PRINT("fGhij"), NULL); } // Overlap leftwards { tickit_renderbuffer_text_at(rb, 0, 0, "Abc"); tickit_renderbuffer_text_at(rb, 0, 3, "Def"); tickit_renderbuffer_text_at(rb, 0, 6, "Ghi"); tickit_renderbuffer_copyrect(rb, &(TickitRect){ .top = 0, .left = 0, .lines = 1, .cols = 6 }, &(TickitRect){ .top = 0, .left = 3, .lines = 1, .cols = 6 }); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer copyrect can copy leftwards", GOTO(0,0), SETPEN(), PRINT("Def"), SETPEN(), PRINT("Ghi"), SETPEN(), PRINT("Ghi"), NULL); } // Overlap rightwards { tickit_renderbuffer_text_at(rb, 0, 0, "aBc"); tickit_renderbuffer_text_at(rb, 0, 3, "dEf"); tickit_renderbuffer_text_at(rb, 0, 6, "gHi"); tickit_renderbuffer_copyrect(rb, &(TickitRect){ .top = 0, .left = 3, .lines = 1, .cols = 6 }, &(TickitRect){ .top = 0, .left = 0, .lines = 1, .cols = 6 }); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer copyrect can copy rightwards", GOTO(0,0), SETPEN(), PRINT("aBc"), SETPEN(), PRINT("aBc"), SETPEN(), PRINT("dEf"), NULL); } // Move { tickit_renderbuffer_text_at(rb, 0, 0, "Hello"); tickit_renderbuffer_char_at(rb, 1, 1, 'A'); tickit_renderbuffer_erase_at(rb, 1, 2, 3); tickit_renderbuffer_moverect(rb, &(TickitRect){ .top = 4, .left = 2, .lines = 2, .cols = 10 }, &(TickitRect){ .top = 0, .left = 0, .lines = 2, .cols = 10 }); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer contents moved after move", GOTO(4,2), SETPEN(), PRINT("Hello"), GOTO(5,3), SETPEN(), PRINT("A"), SETPEN(), ERASECH(3,-1), NULL); } // Move with overlap { tickit_renderbuffer_text_at(rb, 0, 0, "Abcde"); tickit_renderbuffer_moverect(rb, &(TickitRect){ .top = 0, .left = 0, .lines = 1, .cols = 10 }, &(TickitRect){ .top = 0, .left = 3, .lines = 1, .cols = 10 }); tickit_renderbuffer_flush_to_term(rb, tt); is_termlog("RenderBuffer contents moved after move with overlap", GOTO(0,0), SETPEN(), PRINT("de"), NULL); } tickit_renderbuffer_unref(rb); tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/40rootwindow.c0000644000000000000000000000745213613430267014625 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include "taplib-tickit.h" #include "taplib-mockterm.h" int on_event_incr_int(TickitWindow *window, TickitEventFlags flags, void *_info, void *data) { (*(int*)data)++; return 1; } int nextrect = 0; TickitRect rects[4]; int push_on_expose(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; rects[nextrect++] = info->rect; return 1; } int main(int argc, char *argv[]) { TickitTerm *tt = make_term(25, 80); TickitWindow *root = tickit_window_new_root(tt); // Basics { TickitRect geom = tickit_window_get_geometry(root); is_rect(&geom, "0,0+80,25", "root tickit_window_get_geometry"); is_int(tickit_window_top(root), 0, "root tickit_window_top"); is_int(tickit_window_left(root), 0, "root tickit_window_left"); geom = tickit_window_get_abs_geometry(root); is_rect(&geom, "0,0+80,25", "root tickit_window_get_abs_geometry"); is_int(tickit_window_lines(root), 25, "root tickit_window_lines"); is_int(tickit_window_cols(root), 80, "root tickit_window_cols"); is_int(tickit_window_bottom(root), 25, "root tickit_window_bottom"); is_int(tickit_window_right(root), 80, "root tickit_window_right"); ok(tickit_window_parent(root) == NULL, "root tickit_window_parent"); ok(tickit_window_root(root) == root, "root tickit_window_root"); is_int(tickit_window_children(root), 0, "root tickit_window_children"); ok(tickit_window_get_term(root) == tt, "root tickit_window_get_term"); } // Window pen { TickitPen *pen = tickit_window_get_pen(root); ok(!!pen, "window pen"); ok(!tickit_pen_is_nonempty(pen), "pen has no attrs set"); // TODO: effective pen? } // Scrolling { ok(tickit_window_scroll(root, 1, 0), "window can scroll"); tickit_window_flush(root); is_termlog("Termlog scrolled", SETPEN(), SCROLLRECT(0,0,25,80, 1,0), NULL); tickit_window_scrollrect(root, &(TickitRect){ .top = 5, .left = 0, .lines = 10, .cols = 80 }, 3, 0, NULL); tickit_window_flush(root); is_termlog("Termlog after scrollrect", SETPEN(), SCROLLRECT(5,0,10,80, 3,0), NULL); tickit_window_scrollrect(root, &(TickitRect){ .top = 20, .left = 0, .lines = 1, .cols = 80 }, 0, 1, NULL); tickit_window_flush(root); is_termlog("Termlog after scrollrect rightward", SETPEN(), SCROLLRECT(20,0,1,80, 0,1), NULL); tickit_window_scrollrect(root, &(TickitRect){ .top = 21, .left = 10, .lines = 1, .cols = 70 }, 0, -1, NULL); tickit_window_flush(root); is_termlog("Termlog after scrollrect leftward not fullwidth", SETPEN(), SCROLLRECT(21,10,1,70, 0,-1), NULL); } // Resize events { int geom_changed = 0; tickit_window_bind_event(root, TICKIT_WINDOW_ON_GEOMCHANGE, 0, &on_event_incr_int, &geom_changed); is_int(geom_changed, 0, "geometry not yet changed"); tickit_window_bind_event(root, TICKIT_WINDOW_ON_EXPOSE, 0, &push_on_expose, NULL); tickit_mockterm_resize(tt, 30, 100); tickit_window_flush(root); is_int(tickit_window_lines(root), 30, "root tickit_window_lines after term resize"); is_int(tickit_window_cols(root), 100, "root tickit_window_cols after term resize"); is_int(geom_changed, 1, "geometry changed after term resize"); is_int(nextrect, 2, "two exposed rects after term resize"); is_rect(rects+0, "80,0..100,25", "exposed rects[0]"); is_rect(rects+1, "0,25..100,30", "exposed rects[1]"); } // DESTROY { int destroyed = 0; tickit_window_bind_event(root, TICKIT_WINDOW_ON_DESTROY, 0, &on_event_incr_int, &destroyed); tickit_window_unref(root); ok(destroyed, "TICKIT_WINDOW_ON_DESTROY invoked"); } tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/41window.c0000644000000000000000000001172513613430267013720 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include "taplib-tickit.h" #include "taplib-mockterm.h" int on_event_incr_int(TickitWindow *window, TickitEventFlags flags, void *_info, void *data) { (*(int*)data)++; return 1; } int main(int argc, char *argv[]) { TickitTerm *tt = make_term(25, 80); TickitWindow *root = tickit_window_new_root(tt); TickitWindow *win = tickit_window_new(root, (TickitRect){3, 10, 4, 20}, 0); tickit_window_flush(root); // Basics { TickitRect geom = tickit_window_get_geometry(win); is_rect(&geom, "10,3+20,4", "tickit_window_get_geometry"); is_int(tickit_window_top(win), 3, "tickit_window_top"); is_int(tickit_window_left(win), 10, "tickit_window_left"); geom = tickit_window_get_abs_geometry(win); is_rect(&geom, "10,3+20,4", "tickit_window_get_abs_geometry"); is_int(tickit_window_lines(win), 4, "tickit_window_lines"); is_int(tickit_window_cols(win), 20, "tickit_window_cols"); is_int(tickit_window_bottom(win), 7, "tickit_window_bottom"); is_int(tickit_window_right(win), 30, "tickit_window_right"); ok(tickit_window_parent(win) == root, "tickit_window_parent"); ok(tickit_window_root(win) == root, "tickit_window_root"); is_int(tickit_window_children(win), 0, "tickit_window_children"); is_int(tickit_window_children(root), 1, "root tickit_window_children"); TickitWindow *children[1] = {0}; tickit_window_get_children(root, children, 1); is_ptr(children[0], win, "root tickit_window_get_children result[0]"); ok(tickit_window_get_term(win) == tt, "tickit_window_get_term"); } // Geometry change event { int geom_changed = 0; tickit_window_bind_event(win, TICKIT_WINDOW_ON_GEOMCHANGE, 0, on_event_incr_int, &geom_changed); is_int(geom_changed, 0, "geometry not yet changed"); tickit_window_resize(win, 4, 15); TickitRect geom = tickit_window_get_geometry(win); is_rect(&geom, "10,3+15,4", "tickit_window_get_geometry after resize"); is_int(tickit_window_lines(win), 4, "resize tickit_window_lines after resize"); is_int(tickit_window_cols(win), 15, "resize tickit_window_cols after resize"); is_int(geom_changed, 1, "geometry changed after resize"); tickit_window_reposition(win, 5, 15); geom = tickit_window_get_geometry(win); is_rect(&geom, "15,5+15,4", "tickit_window_get_geometry after reposition"); is_int(tickit_window_top(win), 5, "tickit_window_top after reposition"); is_int(tickit_window_left(win), 15, "tickit_window_left after reposition"); geom = tickit_window_get_abs_geometry(win); is_rect(&geom, "15,5+15,4", "tickit_window_get_abs_geometry after reposition"); is_int(geom_changed, 2, "geometry changed after reposition"); } // sub-window nesting { TickitWindow *subwin = tickit_window_new(win, (TickitRect){2, 2, 1, 10}, 0); tickit_window_flush(root); TickitRect geom = tickit_window_get_geometry(subwin); is_rect(&geom, "2,2+10,1", "nested tickit_window_get_geometry"); is_int(tickit_window_top(subwin), 2, "nested tickit_window_top"); is_int(tickit_window_left(subwin), 2, "nested tickit_window_left"); geom = tickit_window_get_abs_geometry(subwin); is_rect(&geom, "17,7+10,1", "nested tickit_window_get_abs_geometry"); is_int(tickit_window_lines(subwin), 1, "nested tickit_window_lines"); is_int(tickit_window_cols(subwin), 10, "nested tickit_window_cols"); ok(tickit_window_parent(subwin) == win, "nested tickit_window_parent"); ok(tickit_window_root(subwin) == root, "nested tickit_window_root"); tickit_window_unref(subwin); tickit_window_flush(root); } // initially-hidden { TickitWindow *subwin = tickit_window_new(win, (TickitRect){4, 4, 2, 2}, TICKIT_WINDOW_HIDDEN); tickit_window_flush(root); ok(!tickit_window_is_visible(subwin), "initially-hidden window not yet visible"); tickit_window_show(subwin); ok(tickit_window_is_visible(subwin), "initially-hidden window visible after show"); tickit_window_unref(subwin); tickit_window_flush(root); } // TICKIT_WINDOW_ON_DESTROY { int destroyed = 0; tickit_window_bind_event(win, TICKIT_WINDOW_ON_DESTROY, 0, &on_event_incr_int, &destroyed); tickit_window_unref(win); ok(destroyed, "TICKIT_WINDOW_ON_DESTROY invoked"); } // explicit close { TickitWindow *win = tickit_window_new(root, (TickitRect){1, 1, 4, 4}, 0); is_int(tickit_window_children(root), 1, "root window has 1 child before close()"); int destroyed = 0; tickit_window_bind_event(win, TICKIT_WINDOW_ON_DESTROY, 0, &on_event_incr_int, &destroyed); tickit_window_close(win); is_int(tickit_window_children(root), 0, "root window has 1 child after close()"); ok(!destroyed, "window not destroyed before unref"); // calling close a second time doesn't crash tickit_window_close(win); tickit_window_unref(win); ok(destroyed, "window not destroyed after unref"); } tickit_window_unref(root); tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/42window-expose.c0000644000000000000000000002725113613430267015223 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include "taplib-tickit.h" #include "taplib-mockterm.h" #include // sprintf int on_expose_incr(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { (*(int *)data)++; return 1; } static int next_rect = 0; static TickitRect exposed_rects[16]; int on_expose_pushrect(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; if(next_rect >= sizeof(exposed_rects)/sizeof(exposed_rects[0])) return 0; exposed_rects[next_rect++] = info->rect; return 1; } int on_expose_render_text(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; TickitRenderBuffer *rb = info->rb; switch(*(int *)data) { case 1: tickit_renderbuffer_text_at(rb, 1, 1, "The text"); tickit_renderbuffer_erase_at(rb, 2, 2, 4); break; case 2: for(int line = info->rect.top; line < tickit_rect_bottom(&info->rect); line++) { char buffer[16]; sprintf(buffer, "Line %d", line); tickit_renderbuffer_text_at(rb, line, 0, buffer); } break; case 3: // parent tickit_renderbuffer_text_at(rb, 0, 0, "Parent"); tickit_renderbuffer_text_at(rb, 0, 14, "Parent"); break; case 4: // child tickit_renderbuffer_text_at(rb, 0, 0, "Child"); break; } return 1; } int on_expose_fillX(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; for(int line = info->rect.top; line < tickit_rect_bottom(&info->rect); line++) { char buffer[80]; for(int i = 0; i < info->rect.cols; i++) buffer[i] = 'X'; buffer[info->rect.cols] = 0; tickit_renderbuffer_text_at(info->rb, line, info->rect.left, buffer); } return 1; } int on_expose_textat(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; tickit_renderbuffer_text_at(info->rb, 0, 0, data); return 1; } int main(int argc, char *argv[]) { TickitTerm *tt = make_term(25, 80); TickitWindow *root = tickit_window_new_root(tt); TickitWindow *win = tickit_window_new(root, (TickitRect){3, 10, 4, 20}, 0); tickit_window_flush(root); int root_exposed = 0; tickit_window_bind_event(root, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_incr, &root_exposed); // Basics { int win_exposed = 0; int bind_id = tickit_window_bind_event(win, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_incr, &win_exposed); int bind_id2 = tickit_window_bind_event(win, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_pushrect, NULL); tickit_window_expose(root, NULL); is_int(win_exposed, 0, "EV_EXPOSE not yet invoked"); tickit_window_flush(root); is_int(root_exposed, 1, "root expose count after tick"); is_int(win_exposed, 1, "win expose count after tick"); is_int(next_rect, 1, "pushed 1 exposed rect"); is_rect(exposed_rects+0, "0,0+20,4", "exposed_rects[0]"); next_rect = 0; tickit_window_expose(win, NULL); tickit_window_flush(root); is_int(root_exposed, 2, "root expose count after expose on win"); is_int(win_exposed, 2, "win expose count after expose on win"); is_int(next_rect, 1, "pushed 1 exposed rect"); is_rect(exposed_rects+0, "0,0+20,4", "exposed_rects[0]"); next_rect = 0; tickit_window_expose(root, NULL); tickit_window_expose(win, NULL); tickit_window_flush(root); is_int(root_exposed, 3, "root expose count after expose on root-then-win"); is_int(win_exposed, 3, "win expose count after expose on root-then-win"); tickit_window_expose(win, NULL); tickit_window_expose(root, NULL); tickit_window_flush(root); is_int(root_exposed, 4, "root expose count after expose on win-then-root"); is_int(win_exposed, 4, "win expose count after expose on win-then-root"); tickit_window_hide(win); tickit_window_flush(root); is_int(root_exposed, 5, "root expose count after hide"); is_int(win_exposed, 4, "win expose count after hide"); tickit_window_show(win); tickit_window_flush(root); is_int(root_exposed, 6, "root expose count after show"); is_int(win_exposed, 5, "win expose count after show"); next_rect = 0; tickit_window_expose(win, &(TickitRect){ .top = 0, .left = 0, .lines = 1, .cols = 20 }); tickit_window_expose(win, &(TickitRect){ .top = 2, .left = 0, .lines = 1, .cols = 20 }); tickit_window_flush(root); is_int(win_exposed, 7, "win expose count after expose two regions"); is_int(next_rect, 2, "exposed 2 regions"); is_rect(exposed_rects+0, "0,0+20,1", "exposed_rects[0]"); is_rect(exposed_rects+1, "0,2+20,1", "exposed_rects[1]"); next_rect = 0; tickit_window_expose(root, &(TickitRect){ .top = 0, .left = 0, .lines = 1, .cols = 20 }); tickit_window_expose(win, &(TickitRect){ .top = 0, .left = 5, .lines = 1, .cols = 10 }); tickit_window_flush(root); is_int(win_exposed, 8, "win expose count after expose separate root+win"); is_int(next_rect, 1, "exposed 1 region"); is_rect(exposed_rects+0, "5,0+10,1", "exposed_rects[0]"); next_rect = 0; tickit_window_expose(win, &(TickitRect){ .top = -2, .left = -2, .lines = 50, .cols = 200 }); tickit_window_flush(root); is_int(next_rect, 1, "exposed 1 region"); is_rect(exposed_rects+0, "0,0+20,4", "exposed_rects[0]"); tickit_window_unbind_event_id(win, bind_id); tickit_window_unbind_event_id(win, bind_id2); } // Rendering inside EV_EXPOSE { int idx = 1; int bind_id = tickit_window_bind_event(win, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_render_text, &idx); tickit_window_expose(win, NULL); tickit_window_flush(root); is_termlog("Termlog after Window expose with output", GOTO(4,11), SETPEN(), PRINT("The text"), GOTO(5,12), SETPEN(), ERASECH(4,-1), NULL); idx = 2; tickit_window_expose(win, &(TickitRect){ .top = 0, .left = 0, .lines = 1, .cols = 20 }); tickit_window_expose(win, &(TickitRect){ .top = 2, .left = 0, .lines = 1, .cols = 20 }); tickit_window_flush(root); is_termlog("Termlog after Window expose twice", GOTO(3,10), SETPEN(), PRINT("Line 0"), GOTO(5,10), SETPEN(), PRINT("Line 2"), NULL); tickit_pen_set_colour_attr(tickit_window_get_pen(win), TICKIT_PEN_FG, 5); tickit_window_expose(win, NULL); tickit_window_flush(root); is_termlog("Termlog after Window expose with pen attrs", GOTO(3,10), SETPEN(.fg=5), PRINT("Line 0"), GOTO(4,10), SETPEN(.fg=5), PRINT("Line 1"), GOTO(5,10), SETPEN(.fg=5), PRINT("Line 2"), GOTO(6,10), SETPEN(.fg=5), PRINT("Line 3"), NULL); tickit_window_unbind_event_id(win, bind_id); tickit_pen_clear_attr(tickit_window_get_pen(win), TICKIT_PEN_FG); } // New windows get exposed immediately { next_rect = 0; int bind_id_in_win = tickit_window_bind_event(win, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_pushrect, NULL); TickitWindow *subwin = tickit_window_new(win, (TickitRect){1, 4, 3, 6}, 0); int exposed = 0; tickit_window_bind_event(subwin, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_incr, &exposed); tickit_window_flush(root); is_int(exposed, 1, "New child window is immediately exposed"); is_int(next_rect, 1, "pushed 1 exposed rect"); is_rect(exposed_rects+0, "4,1+6,3", "exposed_rects[0]"); next_rect = 0; tickit_window_unref(subwin); tickit_window_flush(root); is_int(next_rect, 1, "pushed 1 exposed rect"); is_rect(exposed_rects+0, "4,1+6,3", "exposed_rects[0]"); tickit_window_unbind_event_id(win, bind_id_in_win); } // New hidden windows don't get exposed { next_rect = 0; int bind_id_in_win = tickit_window_bind_event(win, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_pushrect, NULL); TickitWindow *subwin = tickit_window_new(win, (TickitRect){2, 6, 3, 6}, TICKIT_WINDOW_HIDDEN); int exposed = 0; tickit_window_bind_event(subwin, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_incr, &exposed); tickit_window_flush(root); is_int(exposed, 0, "New hidden child window is not exposed"); is_int(next_rect, 0, "No exposed rects"); next_rect = 0; tickit_window_unref(subwin); tickit_window_flush(root); is_int(next_rect, 0, "No exposed rects"); tickit_window_unbind_event_id(win, bind_id_in_win); } // Rendering parent and child simultaneously { int idx_in_win = 3; int bind_id_in_win = tickit_window_bind_event(win, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_render_text, &idx_in_win); tickit_window_expose(win, &(TickitRect){ .top = 0, .left = 0, .lines = 1, .cols = 20 }); TickitWindow *sub = tickit_window_new(win, (TickitRect){0, 7, 1, 7}, 0); int idx_in_sub = 4; tickit_window_bind_event(sub, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_render_text, &idx_in_sub); tickit_window_flush(root); is_termlog("Display after simultaneous expose in parent + child", GOTO(3,10), SETPEN(), PRINT("Parent"), GOTO(3,17), SETPEN(), PRINT("Child"), GOTO(3,24), SETPEN(), PRINT("Parent"), NULL); tickit_window_unref(sub); tickit_window_flush(root); tickit_window_unbind_event_id(win, bind_id_in_win); } // Expose count { int exposed = 0; int bind_id = tickit_window_bind_event(win, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_incr, &exposed); for(int i = 0; i < 100; i++) { tickit_window_expose(win, NULL); tickit_window_flush(root); } is_int(exposed, 100, "exposed 100 times"); tickit_window_unbind_event_id(win, bind_id); } // Child masks a hole in parent { int bind_id = tickit_window_bind_event(win, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_fillX, NULL); TickitWindow *sub = tickit_window_new(win, (TickitRect){0, 5, 1, 10}, 0); // no expose event tickit_window_expose(win, &(TickitRect){ .top = 0, .left = 0, .lines = 1, .cols = 80 }); tickit_window_flush(root); is_termlog("Termlog after expose parent with visible child", GOTO(3,10), SETPEN(), PRINT("XXXXX"), GOTO(3,25), SETPEN(), PRINT("XXXXX"), NULL); tickit_window_unref(sub); tickit_window_unbind_event_id(win, bind_id); } tickit_window_unref(win); // Window ordering { TickitWindow *winA = tickit_window_new(root, (TickitRect){0, 0, 4, 80}, 0); TickitWindow *winB = tickit_window_new(root, (TickitRect){0, 0, 4, 80}, TICKIT_WINDOW_LOWEST); TickitWindow *winC = tickit_window_new(root, (TickitRect){0, 0, 4, 80}, TICKIT_WINDOW_LOWEST); tickit_window_flush(root); tickit_window_bind_event(winA, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_textat, "Window A"); tickit_window_bind_event(winB, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_textat, "Window B"); tickit_window_bind_event(winC, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_textat, "Window C"); tickit_window_expose(root, NULL); tickit_window_flush(root); is_termlog("Termlog for overlapping initially", GOTO(0,0), SETPEN(), PRINT("Window A"), NULL); tickit_window_raise(winB); tickit_window_flush(root); is_termlog("Termlog for overlapping after winB raise", GOTO(0,0), SETPEN(), PRINT("Window B"), NULL); tickit_window_lower(winB); tickit_window_flush(root); is_termlog("Termlog for overlapping after winB lower", GOTO(0,0), SETPEN(), PRINT("Window A"), NULL); tickit_window_raise_to_front(winC); tickit_window_flush(root); is_termlog("Termlog for overlapping after winC raise_to_front", GOTO(0,0), SETPEN(), PRINT("Window C"), NULL); tickit_window_unref(winA); tickit_window_unref(winB); tickit_window_unref(winC); } tickit_window_unref(root); tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/43window-scrolling.c0000644000000000000000000001765413613430267015723 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include "taplib-tickit.h" #include "taplib-mockterm.h" static int next_rect = 0; static TickitRect exposed_rects[16]; int on_expose_pushrect(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; if(next_rect >= sizeof(exposed_rects)/sizeof(exposed_rects[0])) return 0; exposed_rects[next_rect++] = info->rect; return 1; } int main(int argc, char *argv[]) { TickitTerm *tt = make_term(25, 80); TickitWindow *root = tickit_window_new_root(tt); { TickitWindow *win = tickit_window_new(root, (TickitRect){3, 10, 4, 30}, 0); tickit_window_flush(root); ok(!tickit_window_scroll(win, 1, 0), "window does not support scrolling"); drain_termlog(); tickit_window_unref(win); tickit_window_flush(root); } // Scrollable window probably needs to be fullwidth TickitWindow *win = tickit_window_new(root, (TickitRect){5, 0, 10, 80}, 0); tickit_window_flush(root); tickit_window_bind_event(win, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_pushrect, NULL); // scroll down { next_rect = 0; ok(tickit_window_scroll(win, 1, 0), "fullwidth window supports vertical scrolling"); tickit_window_flush(root); is_termlog("Termlog after fullwidth scroll downward", SETPEN(), SCROLLRECT(5,0,10,80, 1,0), NULL); is_int(next_rect, 1, "pushed 1 exposed rect"); is_rect(exposed_rects+0, "0,9+80,1", "exposed_rects[0]"); } // scroll up { next_rect = 0; tickit_window_scroll(win, -1, 0); tickit_window_flush(root); is_termlog("Termlog after fullwidth scroll upward", SETPEN(), SCROLLRECT(5,0,10,80, -1,0), NULL); is_int(next_rect, 1, "pushed 1 exposed rect"); is_rect(exposed_rects+0, "0,0+80,1", "exposed_rects[0]"); } // scroll right { next_rect = 0; ok(tickit_window_scroll(win, 0, 1), "fullwidth window supports horizontal scrolling"); tickit_window_flush(root); is_termlog("Termlog after fullwidth scroll rightward", SETPEN(), SCROLLRECT(5,0,10,80, 0,1), NULL); is_int(next_rect, 1, "pushed 1 exposed rect"); is_rect(exposed_rects+0, "79,0+1,10", "exposed_rects[0]"); } // scroll left { next_rect = 0; tickit_window_scroll(win, 0, -1); tickit_window_flush(root); is_termlog("Termlog after fullwidth scroll leftward", SETPEN(), SCROLLRECT(5,0,10,80, 0,-1), NULL); is_int(next_rect, 1, "pushed 1 exposed rect"); is_rect(exposed_rects+0, "0,0+1,10", "exposed_rects[0]"); } // scrollrect up { next_rect = 0; ok(tickit_window_scrollrect(win, &(TickitRect){ .top = 2, .left = 0, .lines = 3, .cols = 80 }, -1, 0, NULL), "Fullwidth window supports scrolling a region"); tickit_window_flush(root); is_termlog("Termlog after fullwidth scrollrect downward", SETPEN(), SCROLLRECT(7,0,3,80, -1,0), NULL); is_int(next_rect, 1, "pushed 1 exposed rect"); is_rect(exposed_rects+0, "0,2+80,1", "exposed_rects[0]"); } // scrollrect down { next_rect = 0; tickit_window_scrollrect(win, &(TickitRect){ .top = 2, .left = 0, .lines = 3, .cols = 80 }, 1, 0, NULL); tickit_window_flush(root); is_termlog("Termlog after fullwidth scrollrect upward", SETPEN(), SCROLLRECT(7,0,3,80, 1,0), NULL); is_int(next_rect, 1, "pushed 1 exposed rect"); is_rect(exposed_rects+0, "0,4+80,1", "exposed_rects[0]"); } // scrollrect further than area just exposes { next_rect = 0; tickit_window_scrollrect(win, &(TickitRect){ .top = 2, .left = 0, .lines = 3, .cols = 80 }, 5, 0, NULL); tickit_window_flush(root); is_termlog("Termlog empty after scrollrect further than area", NULL); is_int(next_rect, 1, "pushed 1 exposed rect"); is_rect(exposed_rects+0, "0,2+80,3", "exposed_rects[0]"); } // scrollrect with pending damage { // outside tickit_window_expose(win, &(TickitRect){ .top = 1, .left = 4, .lines = 2, .cols = 10 }); // split tickit_window_expose(win, &(TickitRect){ .top = 3, .left = 10, .lines = 3, .cols = 5 }); // inside tickit_window_expose(win, &(TickitRect){ .top = 5, .left = 20, .lines = 2, .cols = 10 }); next_rect = 0; tickit_window_scrollrect(win, &(TickitRect){ .top = 4, .left = 0, .lines = 6, .cols = 80 }, 2, 0, NULL); tickit_window_flush(root); is_termlog("Termlog after scrollrect with impending damage", SETPEN(), SCROLLRECT(9,0,6,80, 2,0), NULL); is_int(next_rect, 4, "pushed 4 exposed rects"); // outside is_rect(exposed_rects+0, "4,1+10,2", "exposed_rects[0]"); // split part outside is_rect(exposed_rects+1, "10,3+5,1", "exposed_rects[1]"); // translated+truncated inside is_rect(exposed_rects+2, "20,4+10,1", "exposed_rects[2]"); // exposed is_rect(exposed_rects+3, "0,8+80,2", "exposed_rects[3]"); } // Child to obscure part of it { TickitWindow *child = tickit_window_new(win, (TickitRect){0, 0, 3, 10}, 0); tickit_window_flush(root); next_rect = 0; tickit_window_scroll(win, 0, 4); tickit_window_flush(root); is_termlog("Termlog after scroll with obscuring child", SETPEN(), SCROLLRECT(5,10,3,70, 0,4), SCROLLRECT(8, 0,7,80, 0,4), NULL); is_int(next_rect, 1, "pushed 1 exposed rect"); is_rect(exposed_rects+0, "76,0+4,10", "exposed_rects[0]"); tickit_window_hide(child); tickit_window_flush(root); next_rect = 0; tickit_window_scroll(win, 0, 4); tickit_window_flush(root); is_termlog("Termlog after scroll with hidden obscuring child", SETPEN(), SCROLLRECT(5,0,10,80, 0,4), NULL); is_int(next_rect, 1, "pushed 1 exposed rect"); is_rect(exposed_rects+0, "76,0+4,10", "exposed_rects[0]"); tickit_window_unref(child); tickit_window_flush(root); } // scroll_with_children { TickitWindow *child = tickit_window_new(win, (TickitRect){0, 70, 1, 10}, 0); tickit_window_flush(root); next_rect = 0; tickit_window_scroll_with_children(win, -2, 0); tickit_window_flush(root); is_termlog("Termlog after scroll_with_children", SETPEN(), SCROLLRECT(5,0,10,80, -2,0), NULL); is_int(next_rect, 1, "pushed 1 exposed rect"); is_rect(exposed_rects+0, "0,0+80,2", "exposed_rects[0]"); tickit_window_unref(child); tickit_window_flush(root); } // sibling to obscure part of it { TickitWindow *sibling = tickit_window_new(root, (TickitRect){0, 0, 10, 20}, 0); tickit_window_flush(root); tickit_window_raise(sibling); tickit_window_flush(root); next_rect = 0; tickit_window_scroll_with_children(win, -2, 0); tickit_window_flush(root); is_termlog("Termlog after scroll_with_children with sibling", SETPEN(), SCROLLRECT(10,0,5,80, -2,0), NULL); is_int(next_rect, 2, "pushed 2 exposed rects"); is_rect(exposed_rects+0, "20,0+60,5", "exposed_rects[0]"); is_rect(exposed_rects+1, "0,5+80,2", "exposed_rects[1]"); tickit_window_hide(sibling); tickit_window_flush(root); next_rect = 0; tickit_window_scroll_with_children(win, -2, 0); tickit_window_flush(root); is_termlog("Termlog after scroll_with_children with hidden sibling", SETPEN(), SCROLLRECT(5,0,10,80, -2,0), NULL); is_int(next_rect, 1, "pushed 1 exposed rect"); is_rect(exposed_rects+0, "0,0+80,2", "exposed_rects[0]"); tickit_window_unref(sibling); tickit_window_flush(root); } // Hidden windows should be ignored { next_rect = 0; tickit_window_hide(win); tickit_window_flush(root); tickit_window_scroll(win, 2, 0); tickit_window_flush(root); is_termlog("Termlog empty after scroll on hidden window", NULL); } tickit_window_unref(win); tickit_window_unref(root); tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/44window-focus.c0000644000000000000000000002013013613430267015026 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include "taplib-mockterm.h" int on_focus(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { *(int *)data = ((TickitFocusEventInfo *)_info)->type == TICKIT_FOCUSEV_IN ? 1 : -1; return 1; } int next_event = 0; static struct { int type; TickitWindow *win; TickitWindow *focuswin; } focus_events[16]; int on_focus_push(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitFocusEventInfo *info = _info; if(next_event > sizeof(focus_events)/sizeof(focus_events[0])) return 0; focus_events[next_event].type = info->type; focus_events[next_event].win = win; focus_events[next_event].focuswin = info->win; next_event++; return 1; } int main(int argc, char *argv[]) { TickitTerm *tt = make_term(25, 80); TickitWindow *root = tickit_window_new_root(tt); TickitWindow *win = tickit_window_new(root, (TickitRect){3, 10, 4, 20}, 0); int focused; tickit_window_bind_event(win, TICKIT_WINDOW_ON_FOCUS, 0, &on_focus, &focused); int value; // Basics { tickit_term_getctl_int(tt, TICKIT_TERMCTL_CURSORVIS, &value); ok(!value, "cursor not yet visible initially"); tickit_window_flush(root); ok(!tickit_window_is_focused(win), "window does not yet have focus"); tickit_window_set_cursor_position(win, 0, 0); tickit_window_flush(root); ok(!tickit_window_is_focused(win), "window still unfocused after set cursor position"); tickit_window_take_focus(win); // no flush ok(tickit_window_is_focused(win), "window immediately has focus after take_focus"); is_int(focused, 1, "window receives FOCUS_IN event"); focused = 0; tickit_window_flush(root); is_termlog("Termlog after focus", GOTO(3,10), NULL); tickit_term_getctl_int(tt, TICKIT_TERMCTL_CURSORVIS, &value); ok(value, "Terminal cursor visible after window focus"); tickit_window_reposition(win, 5, 15); tickit_window_flush(root); is_termlog("Termlog after window reposition", GOTO(5,15), NULL); tickit_window_set_cursor_position(win, 2, 2); tickit_window_flush(root); is_termlog("Termlog after set cursor position", GOTO(7,17), NULL); tickit_window_set_cursor_shape(win, TICKIT_CURSORSHAPE_UNDER); tickit_window_flush(root); is_termlog("Termlog after cursor_shape", GOTO(7,17), NULL); tickit_term_getctl_int(tt, TICKIT_TERMCTL_CURSORSHAPE, &value); is_int(value, TICKIT_CURSORSHAPE_UNDER, "Cursor shape after cursor_shape"); tickit_window_set_cursor_visible(win, false); tickit_window_flush(root); is_termlog("Termlog empty after cursor_visible false", NULL); tickit_term_getctl_int(tt, TICKIT_TERMCTL_CURSORVIS, &value); ok(!value, "Cursor is invisible after cursor_visible false"); tickit_window_set_cursor_visible(win, true); tickit_window_hide(win); tickit_window_flush(root); tickit_term_getctl_int(tt, TICKIT_TERMCTL_CURSORVIS, &value); ok(!value, "Cursor is invisible after focus window hide"); tickit_window_show(win); tickit_window_flush(root); tickit_term_getctl_int(tt, TICKIT_TERMCTL_CURSORVIS, &value); ok(value, "Cursor is visible after focus window show"); is_termlog("Termlog after focus window show", GOTO(7,17), NULL); } // Obscuring by child { TickitWindow *child = tickit_window_new(win, (TickitRect){1, 1, 4, 4}, 0); tickit_window_flush(root); tickit_term_getctl_int(tt, TICKIT_TERMCTL_CURSORVIS, &value); ok(!value, "Cursor is invisible after covering by child window"); tickit_window_hide(child); tickit_window_flush(root); tickit_term_getctl_int(tt, TICKIT_TERMCTL_CURSORVIS, &value); ok(value, "Cursor is visible again after lowering child window"); tickit_window_unref(child); tickit_window_flush(root); drain_termlog(); } // Obscuring by sibling { TickitWindow *sib = tickit_window_new(root, (TickitRect){6, 0, 2, 40}, 0); tickit_window_raise(sib); tickit_window_flush(root); tickit_term_getctl_int(tt, TICKIT_TERMCTL_CURSORVIS, &value); ok(!value, "Cursor is invisible after covering by sibling window"); tickit_window_lower(sib); tickit_window_flush(root); tickit_term_getctl_int(tt, TICKIT_TERMCTL_CURSORVIS, &value); ok(value, "Cursor is visible again after lowering sibling window"); tickit_window_unref(sib); tickit_window_flush(root); drain_termlog(); } { TickitWindow *winA = tickit_window_new(root, (TickitRect){5, 0, 1, 80}, 0); TickitWindow *winB = tickit_window_new(root, (TickitRect){6, 0, 1, 80}, 0); tickit_window_set_cursor_position(winA, 0, 0); tickit_window_set_cursor_position(winB, 0, 0); int focusA = 0; int focusB = 0; tickit_window_bind_event(winA, TICKIT_WINDOW_ON_FOCUS, 0, &on_focus, &focusA); tickit_window_bind_event(winB, TICKIT_WINDOW_ON_FOCUS, 0, &on_focus, &focusB); tickit_window_take_focus(winA); tickit_window_flush(root); is_int(focusA, 1, "focusA after winA takes focus"); is_int(focusB, 0, "focusB undef after winA takes focus"); is_termlog("Termlog after winA takes focus", GOTO(5,0), NULL); tickit_window_take_focus(winB); tickit_window_flush(root); is_int(focusA, -1, "focusA lost after winB takes focus"); is_int(focusB, 1, "focusB after winB takes focus"); is_termlog("Termlog after winB takes focus", GOTO(6,0), NULL); tickit_window_hide(winB); tickit_window_take_focus(winA); tickit_window_flush(root); is_termlog("Termlog after winB hidden", GOTO(5,0), NULL); tickit_window_hide(winA); tickit_window_show(winB); tickit_window_flush(root); is_termlog("Termlog after winA hidden / winB shown", GOTO(6,0), NULL); tickit_window_take_focus(winA); tickit_window_flush(root); is_termlog("Termlog empty after winA take focus while hidden", NULL); ok(tickit_window_is_focused(winB), "winB still has focus after take focus while hidden"); tickit_window_unref(winA); tickit_window_unref(winB); tickit_window_flush(root); } // Child notifications { TickitWindow *subwin = tickit_window_new(win, (TickitRect){1, 1, 2, 2}, 0); int bind_id = tickit_window_bind_event(win, TICKIT_WINDOW_ON_FOCUS, 0, &on_focus_push, NULL); tickit_window_bind_event(subwin, TICKIT_WINDOW_ON_FOCUS, 0, &on_focus_push, NULL); tickit_window_setctl_int(win, TICKIT_WINCTL_FOCUS_CHILD_NOTIFY, true); tickit_window_flush(root); tickit_window_set_cursor_position(subwin, 0, 0); tickit_window_take_focus(subwin); is_int(next_event, 2, "take_focus pushes two events"); is_int(focus_events[0].type, TICKIT_FOCUSEV_IN, "focus_events[0].type"); is_ptr(focus_events[0].win, win, "focus_events[0].win"); is_ptr(focus_events[0].focuswin, subwin, "focus_events[0].focuswin"); is_int(focus_events[1].type, TICKIT_FOCUSEV_IN, "focus_events[1].type"); is_ptr(focus_events[1].win, subwin, "focus_events[1].win"); is_ptr(focus_events[1].focuswin, subwin, "focus_events[1].focuswin"); TickitWindow *otherwin = tickit_window_new(root, (TickitRect){0, 0, 1, 1}, 0); tickit_window_flush(root); next_event = 0; tickit_window_take_focus(otherwin); is_int(next_event, 2, "losing focus pushes two events"); is_int(focus_events[0].type, TICKIT_FOCUSEV_OUT, "focus_events[0].type"); is_ptr(focus_events[0].win, subwin, "focus_events[0].win"); is_ptr(focus_events[0].focuswin, subwin, "focus_events[0].focuswin"); is_int(focus_events[1].type, TICKIT_FOCUSEV_OUT, "focus_events[1].type"); is_ptr(focus_events[1].win, win, "focus_events[1].win"); is_ptr(focus_events[1].focuswin, subwin, "focus_events[1].focuswin"); tickit_window_unref(otherwin); tickit_window_unref(subwin); tickit_window_unbind_event_id(win, bind_id); } tickit_window_unref(win); tickit_window_unref(root); tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/45window-input.c0000644000000000000000000002250113613430267015053 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include "taplib-mockterm.h" #include struct LastEvent { int type; int mod; int line, col, button; char str[16]; int ret; }; int on_key_event_capture(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { struct LastEvent *last_event = data; TickitKeyEventInfo *info = _info; last_event->type = info->type; last_event->mod = info->mod; strcpy(last_event->str, info->str); return last_event->ret; } int on_mouse_event_capture(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { struct LastEvent *last_event = data; TickitMouseEventInfo *info = _info; last_event->type = info->type; last_event->mod = info->mod; last_event->line = info->line; last_event->col = info->col; last_event->button = info->button; return last_event->ret; } int next_idx = 0; char *ids[3]; int on_event_push(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { ids[next_idx++] = data; return 0; } int on_event_incr_int(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { ((int *)data)[0]++; return 0; } int on_termevent_incr_int(TickitTerm *tt, TickitEventFlags flags, void *_info, void *data) { ((int *)data)[0]++; return 0; } TickitWindow *childwin = NULL; int childmouse = 0; int win_on_mouse_child(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { if(childwin) return 0; childwin = tickit_window_new(win, (TickitRect){0, 0, 2, 2}, 0); tickit_window_bind_event(childwin, TICKIT_WINDOW_ON_MOUSE, 0, &on_event_incr_int, &childmouse); return 0; } TickitWindow *siblingwin = NULL; int siblingmouse = 0; int win_on_mouse_sibling(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { if(siblingwin) return 0; siblingwin = tickit_window_new(win, (TickitRect){0, 0, 2, 2}, 0); tickit_window_bind_event(siblingwin, TICKIT_WINDOW_ON_MOUSE, 0, &on_event_incr_int, &siblingmouse); return 0; } int main(int argc, char *argv[]) { TickitTerm *tt = make_term(25, 80); TickitWindow *root = tickit_window_new_root(tt); TickitWindow *win = tickit_window_new(root, (TickitRect){3, 10, 4, 20}, 0); tickit_window_take_focus(win); tickit_window_flush(root); struct LastEvent win_last = { .ret = 1 }; int key_bind_id = tickit_window_bind_event(win, TICKIT_WINDOW_ON_KEY, 0, &on_key_event_capture, &win_last); int mouse_bind_id = tickit_window_bind_event(win, TICKIT_WINDOW_ON_MOUSE, 0, &on_mouse_event_capture, &win_last); // Key events { press_key(TICKIT_KEYEV_TEXT, "A", 0); is_int(win_last.type, TICKIT_KEYEV_TEXT, "win_last.type for A"); is_str(win_last.str, "A", "win_last.str for A"); press_key(TICKIT_KEYEV_KEY, "C-a", TICKIT_MOD_CTRL); is_int(win_last.type, TICKIT_KEYEV_KEY, "win_last.type for C-a"); is_str(win_last.str, "C-a", "win_last.str for C-a"); is_int(win_last.mod, TICKIT_MOD_CTRL, "win_last.mod for C-a"); } // Mouse events { win_last.type = 0; press_mouse(TICKIT_MOUSEEV_PRESS, 1, 5, 15, 0); is_int(win_last.type, TICKIT_MOUSEEV_PRESS, "win_last.type for press 1@15,5"); is_int(win_last.line, 2, "win_last.line for press 1@15,5"); is_int(win_last.col, 5, "win_last.col for press 1@15,5"); win_last.type = 0; press_mouse(TICKIT_MOUSEEV_PRESS, 1, 1, 2, 0); is_int(win_last.type, 0, "win_last.type still 0 after press @1,2"); } // Unhandled events are not consumed { int term_invoked = 0; int bind_id = tickit_term_bind_event(tt, TICKIT_TERM_ON_KEY, 0, &on_termevent_incr_int, &term_invoked); win_last.ret = 0; press_key(TICKIT_KEYEV_KEY, "A", 0); ok(term_invoked, "term event handler invoked after unhandled window event"); tickit_term_unbind_event_id(tt, bind_id); } TickitWindow *subwin = tickit_window_new(win, (TickitRect){2, 2, 1, 10}, 0); // Subwindow { tickit_window_take_focus(subwin); tickit_window_flush(root); struct LastEvent subwin_last = { .ret = 1 }; int sub_key_bind_id = tickit_window_bind_event(subwin, TICKIT_WINDOW_ON_KEY, 0, &on_key_event_capture, &subwin_last); int sub_mouse_bind_id = tickit_window_bind_event(subwin, TICKIT_WINDOW_ON_MOUSE, 0, &on_mouse_event_capture, &subwin_last); win_last.type = 0; press_key(TICKIT_KEYEV_TEXT, "B", 0); is_int(subwin_last.type, TICKIT_KEYEV_TEXT, "subwin_last.type for B"); is_str(subwin_last.str, "B", "subwin_last.str for B"); is_int(win_last.type, 0, "win_last.type for B"); subwin_last.type = 0; press_mouse(TICKIT_MOUSEEV_PRESS, 1, 5, 15, 0); is_int(subwin_last.type, TICKIT_MOUSEEV_PRESS, "subwin_last.type for press 1@15,5"); is_int(subwin_last.line, 0, "subwin_last.line for press 1@15,5"); is_int(subwin_last.col, 3, "subwin_last.col for press 1@15,5"); subwin_last.ret = 0; subwin_last.type = 0; win_last.type = 0; press_key(TICKIT_KEYEV_TEXT, "C", 0); is_int(subwin_last.type, TICKIT_KEYEV_TEXT, "subwin_last.type for C"); is_str(subwin_last.str, "C", "subwin_last.str for C"); is_int(win_last.type, TICKIT_KEYEV_TEXT, "win_last.type for C"); is_str(win_last.str, "C", "win_last.str for C"); tickit_window_unbind_event_id(subwin, sub_key_bind_id); tickit_window_unbind_event_id(subwin, sub_mouse_bind_id); } tickit_window_unbind_event_id(win, key_bind_id); tickit_window_unbind_event_id(win, mouse_bind_id); // Event ordering { TickitWindow *otherwin = tickit_window_new(root, (TickitRect){10, 10, 4, 20}, 0); tickit_window_flush(root); int bind_ids[] = { tickit_window_bind_event(win, TICKIT_WINDOW_ON_KEY, 0, &on_event_push, "win"), tickit_window_bind_event(subwin, TICKIT_WINDOW_ON_KEY, 0, &on_event_push, "subwin"), tickit_window_bind_event(otherwin, TICKIT_WINDOW_ON_KEY, 0, &on_event_push, "otherwin"), }; press_key(TICKIT_KEYEV_TEXT, "D", 0); is_int(next_idx, 3, "press_key pushes 3 strings for D"); is_str(ids[0], "subwin", "ids[0] for D"); is_str(ids[1], "win", "ids[1] for D"); is_str(ids[2], "otherwin", "ids[2] for D"); tickit_window_hide(subwin); next_idx = 0; press_key(TICKIT_KEYEV_TEXT, "E", 0); is_int(next_idx, 2, "press_key pushes 2 strings for E"); is_str(ids[0], "win", "ids[0] for E"); is_str(ids[1], "otherwin", "ids[1] for E"); tickit_window_unref(otherwin); tickit_window_flush(root); tickit_window_unbind_event_id(win, bind_ids[0]); tickit_window_unbind_event_id(subwin, bind_ids[1]); } tickit_window_unref(subwin); // Windows created in input events handlers don't receive events // child windows { int id = tickit_window_bind_event(win, TICKIT_WINDOW_ON_MOUSE, 0, &win_on_mouse_child, NULL); press_mouse(TICKIT_MOUSEEV_PRESS, 1, 3, 10, 0); ok(!!childwin, "child window created"); is_int(childmouse, 0, "child window has not yet received a mouse event"); tickit_window_flush(root); press_mouse(TICKIT_MOUSEEV_PRESS, 1, 3, 10, 0); is_int(childmouse, 1, "child window has now received an event after flush"); tickit_window_unbind_event_id(win, id); tickit_window_unref(childwin); tickit_window_flush(root); } // sibling windows { int id = tickit_window_bind_event(win, TICKIT_WINDOW_ON_MOUSE, 0, &win_on_mouse_sibling, NULL); press_mouse(TICKIT_MOUSEEV_PRESS, 1, 3, 10, 0); ok(!!siblingwin, "sibling window created"); is_int(siblingmouse, 0, "sibling window has not yet received a mouse event"); tickit_window_flush(root); press_mouse(TICKIT_MOUSEEV_PRESS, 1, 3, 10, 0); is_int(siblingmouse, 1, "sibling window has now received an event after flush"); tickit_window_unbind_event_id(win, id); tickit_window_unref(siblingwin); tickit_window_flush(root); } // STEAL_INPUT { TickitWindow *thief = tickit_window_new(root, (TickitRect){2, 5, 3, 3}, TICKIT_WINDOW_STEAL_INPUT); struct LastEvent thief_last = { .ret = 1 }; tickit_window_bind_event(thief, TICKIT_WINDOW_ON_KEY, 0, &on_key_event_capture, &thief_last); tickit_window_bind_event(thief, TICKIT_WINDOW_ON_MOUSE, 0, &on_mouse_event_capture, &thief_last); ok(tickit_window_is_steal_input(thief), "tickit_window_is_steal_input() returns true"); press_key(TICKIT_KEYEV_TEXT, "D", 0); is_int(thief_last.type, TICKIT_KEYEV_TEXT, "thief_last.type for D"); is_str(thief_last.str, "D", "thief_last.str for D"); press_mouse(TICKIT_MOUSEEV_PRESS, 1, 1, 1, 0); is_int(thief_last.type, TICKIT_MOUSEEV_PRESS, "thief_last.type for press 1@1,1"); is_int(thief_last.line, -1, "thief_last.line for press 1@1,1"); is_int(thief_last.col, -4, "thief_last.col for press 1@1,1"); thief_last.type = 0; win_last.type = 0; tickit_window_setctl_int(thief, TICKIT_WINCTL_STEAL_INPUT, false); ok(!tickit_window_is_steal_input(thief), "tickit_window_is_steal_input() returns false"); press_mouse(TICKIT_MOUSEEV_PRESS, 1, 1, 1, 0); is_int(thief_last.type, 0, "thief does not see event after disabling STEAL_INPUT"); tickit_window_unref(thief); } tickit_window_unref(win); tickit_window_unref(root); tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/46window-float.c0000644000000000000000000001236013613430267015024 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include "taplib-tickit.h" #include "taplib-mockterm.h" int on_expose_fillchr(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; for(int line = info->rect.top; line < tickit_rect_bottom(&info->rect); line++) { char buffer[90]; for(int i = 0; i < info->rect.cols; i++) buffer[i] = *(char *)data; buffer[info->rect.cols] = 0; tickit_renderbuffer_text_at(info->rb, line, info->rect.left, buffer); } return 1; } int on_expose_textat(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; tickit_renderbuffer_text_at(info->rb, 0, 0, data); return 1; } static int next_rect = 0; static TickitRect exposed_rects[16]; int on_expose_pushrect(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitExposeEventInfo *info = _info; if(next_rect >= sizeof(exposed_rects)/sizeof(exposed_rects[0])) return 0; exposed_rects[next_rect++] = info->rect; return 1; } int on_key_input_capture(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { *((TickitKeyEventInfo *)data) = *(TickitKeyEventInfo *)_info; return 1; } int on_mouse_input_capture(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { *((TickitMouseEventInfo *)data) = *(TickitMouseEventInfo *)_info; return 1; } int main(int argc, char *argv[]) { TickitTerm *tt = make_term(25, 80); TickitWindow *root = tickit_window_new_root(tt); TickitWindow *rootfloat = tickit_window_new(root, (TickitRect){10, 10, 5, 30}, 0); tickit_window_flush(root); // Basics { int bind_id = tickit_window_bind_event(root, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_fillchr, "X"); tickit_window_expose(root, &(TickitRect){ .top = 10, .lines = 1, .left = 0, .cols = 80 }); tickit_window_flush(root); is_termlog("Termlog for print under floating window", GOTO(10,0), SETPEN(), PRINT("XXXXXXXXXX"), GOTO(10,40), SETPEN(), PRINT("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"), NULL); TickitWindow *win = tickit_window_new(root, (TickitRect){10, 20, 1, 50}, TICKIT_WINDOW_LOWEST); tickit_window_bind_event(win, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_fillchr, "Y"); tickit_window_expose(win, NULL); tickit_window_flush(root); is_termlog("Termlog for print sibling under floating window", GOTO(10,40), SETPEN(), PRINT("YYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"), NULL); TickitWindow *popupwin = tickit_window_new(win, (TickitRect){2, 2, 10, 10}, TICKIT_WINDOW_POPUP); tickit_window_flush(root); TickitRect geom = tickit_window_get_abs_geometry(popupwin); is_rect(&geom, "22,12+10,10", "popupwin abs_geometry"); TickitKeyEventInfo keyinfo; tickit_window_bind_event(popupwin, TICKIT_WINDOW_ON_KEY, 0, &on_key_input_capture, &keyinfo); press_key(TICKIT_KEYEV_TEXT, "G", 0); is_int(keyinfo.type, TICKIT_KEYEV_TEXT, "key type after press_key on popupwin"); TickitMouseEventInfo mouseinfo; tickit_window_bind_event(popupwin, TICKIT_WINDOW_ON_MOUSE, 0, &on_mouse_input_capture, &mouseinfo); press_mouse(TICKIT_MOUSEEV_PRESS, 1, 5, 12, 0); is_int(mouseinfo.type, TICKIT_MOUSEEV_PRESS, "mouse type after press_mouse on popupwin"); is_int(mouseinfo.line, -7, "mouse line after press_mouse on popupwin"); is_int(mouseinfo.col, -10, "mouse column after press_mouse on popupwin"); tickit_window_unref(popupwin); tickit_window_unref(win); tickit_window_unbind_event_id(root, bind_id); } // Drawing on floats { int bind_id = tickit_window_bind_event(rootfloat, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_textat, "|-- Yipee --|"); tickit_window_expose(rootfloat, NULL); tickit_window_flush(root); is_termlog("Termlog for print to floating window", GOTO(10,10), SETPEN(), PRINT("|-- Yipee --|"), NULL); TickitWindow *subwin = tickit_window_new(rootfloat, (TickitRect){0, 4, 1, 6}, 0); tickit_window_bind_event(subwin, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_textat, "Byenow"); tickit_window_expose(subwin, NULL); tickit_window_flush(root); is_termlog("Termlog for print to child of floating window", GOTO(10,14), SETPEN(), PRINT("Byenow"), NULL); tickit_window_unref(subwin); tickit_window_unbind_event_id(rootfloat, bind_id); } // Scrolling with float obscurations { int bind_id = tickit_window_bind_event(root, TICKIT_WINDOW_ON_EXPOSE, 0, &on_expose_pushrect, NULL); tickit_window_flush(root); drain_termlog(); next_rect = 0; tickit_window_scroll(root, 3, 0); tickit_window_flush(root); is_termlog("Termlog after scroll with floats", SETPEN(), SCROLLRECT( 0,0,10,80, 3,0), SCROLLRECT(15,0,10,80, 3,0), NULL); is_int(next_rect, 4, ""); is_rect(exposed_rects+0, "0,7+80,3", "exposed_rects[0]"); is_rect(exposed_rects+1, "0,10+10,5", "exposed_rects[1]"); is_rect(exposed_rects+2, "40,10+40,5", "exposed_rects[2]"); is_rect(exposed_rects+3, "0,22+80,3", "exposed_rects[3]"); tickit_window_unbind_event_id(root, bind_id); } tickit_window_unref(rootfloat); tickit_window_unref(root); tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/47window-dragndrop.c0000644000000000000000000000777013613430267015711 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include "taplib-tickit.h" #include "taplib-mockterm.h" int next_event = 0; struct SavedEvent { TickitWindow *win; int type; int line, col; } events[9]; int on_input_push(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitMouseEventInfo *info = _info; events[next_event].win = win; events[next_event].type = info->type; events[next_event].line = info->line; events[next_event].col = info->col; next_event++; return 1; } void is_event(struct SavedEvent *ev, int type, int line, int col, char *name) { if(ev->type != type) { fail(name); diag("got type=%d, expected type=%d", ev->type, type); return; } if(ev->line != line || ev->col != col) { fail(name); diag("got position=%d,%d, expected position=%d,%d", ev->col, ev->line, col, line); return; } pass(name); } void is_event_win(struct SavedEvent *ev, int type, int line, int col, TickitWindow *win, char *name) { if(ev->win != win) { fail(name); diag("got win=%p, expected win=%p", ev->win, win); return; } is_event(ev, type, line, col, name); } int main(int argc, char *argv[]) { TickitTerm *tt = make_term(25, 80); TickitWindow *root = tickit_window_new_root(tt); // dragging mouse within one window { int event_id = tickit_window_bind_event(root, TICKIT_WINDOW_ON_MOUSE, 0, &on_input_push, NULL); next_event = 0; press_mouse(TICKIT_MOUSEEV_PRESS, 1, 2, 5, 0); is_int(next_event, 1, "pushed 1 event after mouse press"); is_event(events+0, TICKIT_MOUSEEV_PRESS, 2, 5, "event[0] after mouse press"); next_event = 0; press_mouse(TICKIT_MOUSEEV_DRAG, 1, 3, 5, 0); is_int(next_event, 2, "pushed 2 events after drag"); is_event(events+0, TICKIT_MOUSEEV_DRAG_START, 2, 5, "event[0] after mouse drag"); is_event(events+1, TICKIT_MOUSEEV_DRAG, 3, 5, "event[1] after mouse drag"); next_event = 0; press_mouse(TICKIT_MOUSEEV_RELEASE, 1, 3, 5, 0); is_int(next_event, 3, "pushed 3 events after release"); is_event(events+0, TICKIT_MOUSEEV_DRAG_DROP, 3, 5, "event[0] after mouse release"); is_event(events+1, TICKIT_MOUSEEV_DRAG_STOP, 3, 5, "event[1] after mouse release"); is_event(events+2, TICKIT_MOUSEEV_RELEASE, 3, 5, "event[2] after mouse release"); tickit_window_unbind_event_id(root, event_id); } // dragging between windows { TickitWindow *winA = tickit_window_new(root, (TickitRect){ 0, 0, 10, 80}, 0); TickitWindow *winB = tickit_window_new(root, (TickitRect){15, 0, 10, 80}, 0); tickit_window_bind_event(winA, TICKIT_WINDOW_ON_MOUSE, 0, &on_input_push, NULL); tickit_window_bind_event(winB, TICKIT_WINDOW_ON_MOUSE, 0, &on_input_push, NULL); next_event = 0; press_mouse(TICKIT_MOUSEEV_PRESS, 1, 5, 20, 0); press_mouse(TICKIT_MOUSEEV_DRAG, 1, 8, 20, 0); press_mouse(TICKIT_MOUSEEV_DRAG, 1, 12, 20, 0); press_mouse(TICKIT_MOUSEEV_DRAG, 1, 18, 20, 0); press_mouse(TICKIT_MOUSEEV_RELEASE, 1, 18, 20, 0); is_int(next_event, 9, "pushed 9 events"); // press 5,20 is_event_win(events+0, TICKIT_MOUSEEV_PRESS, 5, 20, winA, "event[0]"); // drag 8,20 is_event_win(events+1, TICKIT_MOUSEEV_DRAG_START, 5, 20, winA, "event[1]"); is_event_win(events+2, TICKIT_MOUSEEV_DRAG, 8, 20, winA, "event[2]"); // drag 12,20 is_event_win(events+3, TICKIT_MOUSEEV_DRAG_OUTSIDE, 12, 20, winA, "event[3]"); // drag 18,20 is_event_win(events+4, TICKIT_MOUSEEV_DRAG, 3, 20, winB, "event[4]"); is_event_win(events+5, TICKIT_MOUSEEV_DRAG_OUTSIDE, 18, 20, winA, "event[5]"); // release 18,20 is_event_win(events+6, TICKIT_MOUSEEV_DRAG_DROP, 3, 20, winB, "event[6]"); is_event_win(events+7, TICKIT_MOUSEEV_DRAG_STOP, 18, 20, winA, "event[7]"); is_event_win(events+8, TICKIT_MOUSEEV_RELEASE, 3, 20, winB, "event[8]"); tickit_window_unref(winA); tickit_window_unref(winB); } tickit_window_unref(root); tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/48window-refcount.c0000644000000000000000000000424213613430267015546 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include "taplib-mockterm.h" /* * This test file mostly can't detect its own errors. It might segfault if the * library can't cope. Alternatively, run it via valgrind * * $ LD_LIBRARY_PATH=.libs valgrind t/.libs/48window-refcount.t */ int on_event_close(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { TickitWindow *target = data; tickit_window_close(target); tickit_window_unref(target); return 0; } int on_event_incr(TickitWindow *win, TickitEventFlags flags, void *_info, void *data) { (*((int *)data))++; return 0; } int main(int argc, char *argv[]) { TickitTerm *tt = make_term(25, 80); TickitWindow *root = tickit_window_new_root(tt); // Windows can close themeselves in event handler without crashing { TickitWindow *win = tickit_window_new(root, (TickitRect){3, 10, 4, 20}, 0); tickit_window_take_focus(win); tickit_window_flush(root); tickit_window_bind_event(win, TICKIT_WINDOW_ON_KEY, 0, &on_event_close, win); press_key(TICKIT_KEYEV_TEXT, "A", 0); tickit_window_flush(root); // We can't really tell here if it worked, but it hasn't crashed. pass("Window can close itself in event handler"); } // Next window sibling unaffected by destruction { TickitWindow *win1 = tickit_window_new(root, (TickitRect){3, 10, 4, 20}, 0); TickitWindow *win2 = tickit_window_new(root, (TickitRect){7, 10, 4, 20}, 0); tickit_window_flush(root); tickit_window_bind_event(win1, TICKIT_WINDOW_ON_KEY, 0, &on_event_close, win1); int count = 0; tickit_window_bind_event(win2, TICKIT_WINDOW_ON_KEY, 0, &on_event_incr, &count); press_key(TICKIT_KEYEV_TEXT, "A", 0); tickit_window_flush(root); is_int(count, 1, "Event handler in sibling window is invoked"); tickit_window_unref(win2); } // Orphan windows can safely outlive their parent { TickitWindow *orphanwin = tickit_window_new(root, (TickitRect){10, 1, 2, 2}, 0); tickit_window_ref(orphanwin); tickit_window_flush(root); // DESTROY ROOT tickit_window_unref(root); tickit_window_unref(orphanwin); } tickit_term_unref(tt); return exit_status(); } libtickit-0.3.4/t/50tickit.c0000644000000000000000000000071113613430267013671 0ustar 00000000000000#include "tickit.h" #include "tickit-mockterm.h" #include "taplib.h" int main(int argc, char *argv[]) { Tickit *t = tickit_new_for_term(tickit_mockterm_new(25, 80)); ok(!!t, "tickit_new_stdio()"); /* TODO: add some real tests * There isn't a lot that can be checked in a unit-test script yet, until * more accessors, mutators, and maybe some construction options or a * builder happens */ tickit_unref(t); return exit_status(); } libtickit-0.3.4/t/51tickit-io.c0000644000000000000000000000170113613430267014277 0ustar 00000000000000#include "tickit.h" #include "tickit-mockterm.h" #include "taplib.h" #include static int on_call_incr(Tickit *t, TickitEventFlags flags, void *info, void *user) { if(flags & TICKIT_EV_FIRE) { int *ip = user; (*ip)++; tickit_stop(t); } return 1; } int main(int argc, char *argv[]) { Tickit *t = tickit_new_for_term(tickit_mockterm_new(25, 80)); { int fds[2]; if(pipe(fds) != 0) { perror("pipe"); exit(1); } int counter = 0; void *watch = tickit_watch_io_read(t, fds[0], 0, &on_call_incr, &counter); write(fds[1], "OUT\n", 4); tickit_run(t); is_int(counter, 1, "tickit_watch_io_read invokes callback"); tickit_watch_cancel(t, watch); tickit_tick(t, TICKIT_RUN_NOHANG); is_int(counter, 1, "tickit_cancel_watch removes iowatch"); /* TODO: fds[0] is still readready currently but lets just throw it away */ } tickit_unref(t); return exit_status(); } libtickit-0.3.4/t/52tickit-timer.c0000644000000000000000000000527413613430267015022 0ustar 00000000000000#include "tickit.h" #include "tickit-mockterm.h" #include "taplib.h" static int unbound_count; static int on_call_incr(Tickit *t, TickitEventFlags flags, void *info, void *user) { if(flags & TICKIT_EV_FIRE) { int *ip = user; (*ip)++; tickit_stop(t); } if(flags & TICKIT_EV_UNBIND) unbound_count++; return 1; } struct State { int *counterp; int capture; }; static int on_call_capture(Tickit *t, TickitEventFlags flags, void *info, void *user) { struct State *state = user; state->capture = *state->counterp; (*state->counterp)--; if(!(*state->counterp)) tickit_stop(t); return 1; } int main(int argc, char *argv[]) { Tickit *t = tickit_new_for_term(tickit_mockterm_new(25, 80)); /* tickit_watch_timer_after_msec */ { int called = 0; tickit_watch_timer_after_msec(t, 10, 0, &on_call_incr, &called); tickit_run(t); is_int(called, 1, "tickit_watch_timer_after_msec invokes callback"); } /* tickit_watch_timer_at_tv */ { struct timeval tv; gettimeofday(&tv, NULL); tv.tv_usec += 1000; if(tv.tv_usec >= 1000000) tv.tv_sec++, tv.tv_usec -= 1000000; int called = 0; tickit_watch_timer_at_tv(t, &tv, 0, &on_call_incr, &called); tickit_run(t); is_int(called, 1, "tickit_watch_timer_at_tv invokes callback"); } /* two timers with ordering */ { int counter = 2; struct State state_a = { .counterp = &counter }, state_b = { .counterp = &counter }; tickit_watch_timer_after_msec(t, 10, 0, &on_call_capture, &state_a); tickit_watch_timer_after_msec(t, 20, 0, &on_call_capture, &state_b); tickit_run(t); is_int(state_a.capture, 2, "tickit_watch_timer_after_msec first capture"); is_int(state_b.capture, 1, "tickit_watch_timer_after_msec second capture"); } /* timer cancellation */ { void *watch = tickit_watch_timer_after_msec(t, 5, TICKIT_BIND_UNBIND, &on_call_incr, NULL); tickit_watch_cancel(t, watch); pass("immediate tickit_watch_cancel does not segfault"); } { int called = 0; tickit_watch_timer_after_msec(t, 10, 0, &on_call_incr, &called); int not_called = 0; void *watch = tickit_watch_timer_after_msec(t, 5, TICKIT_BIND_UNBIND, &on_call_incr, ¬_called); unbound_count = 0; tickit_watch_cancel(t, watch); is_int(unbound_count, 1, "unbound_count after tickit_watch_cancel"); tickit_run(t); ok(!not_called, "tickit_watch_cancel prevents invocation"); } /* object destruction */ { tickit_watch_timer_after_msec(t, 10, TICKIT_BIND_DESTROY, &on_call_incr, NULL); unbound_count = 0; tickit_unref(t); is_int(unbound_count, 1, "unbound_count after tickit_unref"); } return exit_status(); } libtickit-0.3.4/t/53tickit-later.c0000644000000000000000000000227513613430267015010 0ustar 00000000000000#include "tickit.h" #include "tickit-mockterm.h" #include "taplib.h" static int unbound_count; static int on_call_incr(Tickit *t, TickitEventFlags flags, void *info, void *user) { if(flags & TICKIT_EV_FIRE) { int *ip = user; (*ip)++; tickit_stop(t); } if(flags & TICKIT_EV_UNBIND) unbound_count++; return 1; } int main(int argc, char *argv[]) { Tickit *t = tickit_new_for_term(tickit_mockterm_new(25, 80)); { int called = 0; tickit_watch_later(t, 0, &on_call_incr, &called); tickit_run(t); is_int(called, 1, "tickit_watch_later invokes callback"); } /* cancellation */ { int called = 0; void *w = tickit_watch_later(t, TICKIT_BIND_UNBIND, &on_call_incr, &called); unbound_count = 0; tickit_watch_cancel(t, w); tickit_tick(t, TICKIT_RUN_NOHANG); is_int(called, 0, "tickit_watch_cancel removes callback"); is_int(unbound_count, 1, "unbound_count after tickit_watch_cancel"); } /* object destruction */ { tickit_watch_later(t, TICKIT_BIND_DESTROY, &on_call_incr, NULL); unbound_count = 0; tickit_unref(t); is_int(unbound_count, 1, "unbound_count after tickit_unref"); } return exit_status(); } libtickit-0.3.4/t/59tickit-tick.c0000644000000000000000000000135213613430267014634 0ustar 00000000000000#include "tickit.h" #include "tickit-mockterm.h" #include "taplib.h" static int on_call_incr(Tickit *t, TickitEventFlags flags, void *info, void *user) { if(flags & TICKIT_EV_FIRE) { int *ip = user; (*ip)++; } return 1; } int main(int argc, char *argv[]) { Tickit *t = tickit_new_for_term(tickit_mockterm_new(25, 80)); // RUN_NOHANG with nothing { tickit_tick(t, TICKIT_RUN_NOHANG); pass("tickit_tick TICKIT_RUN_NOHANG stops immediately"); } // RUN_ONCE runs once { int called = 0; tickit_watch_later(t, 0, &on_call_incr, &called); tickit_tick(t, TICKIT_RUN_ONCE); is_int(called, 1, "tickit_tick TICKIT_RUN_ONCE stops after invocation"); } tickit_unref(t); return exit_status(); } libtickit-0.3.4/t/60tickit-setup.c0000644000000000000000000000306013613430267015030 0ustar 00000000000000#include "tickit.h" #include "taplib.h" #include void output(TickitTerm *tt, const char *bytes, size_t len, void *user) { char *buffer = user; strncat(buffer, bytes, len); } int main(int argc, char *argv[]) { char buffer[1024] = { 0 }; /* Regular setup */ { TickitTerm *tt = tickit_term_new_for_termtype("xterm"); Tickit *t = tickit_new_for_term(tt); tickit_term_set_output_func(tt, output, buffer); buffer[0] = 0; tickit_tick(t, TICKIT_RUN_NOHANG); /* This test is somewhat fragile, but there's not a lot we can do about * that. It has to be */ is_str_escape(buffer, "\e[?1049h" // ALTSCREEN on "\e[?25l" // CURSORVIS off "\e[?1002h\e[?1006h" // MOUSE "\e=" // KEYPAD_APP "\e[2J", // clear "buffer after setup"); tickit_unref(t); } { TickitTerm *tt = tickit_term_new_for_termtype("xterm"); Tickit *t = tickit_new_for_term(tt); tickit_term_set_output_func(tt, output, buffer); tickit_setctl_int(t, TICKIT_CTL_USE_ALTSCREEN, 0); buffer[0] = 0; tickit_tick(t, TICKIT_RUN_NOHANG); /* This test is somewhat fragile, but there's not a lot we can do about * that. It has to be */ is_str_escape(buffer, // no ALTSCREEN "\e[?25l" // CURSORVIS off "\e[?1002h\e[?1006h" // MOUSE "\e=" // KEYPAD_APP "\e[2J", // clear "buffer after setup"); tickit_unref(t); } return exit_status(); } libtickit-0.3.4/t/mockterm.c0000644000000000000000000000755513613430267014073 0ustar 00000000000000#include "taplib-mockterm.h" #include "taplib.h" #include "tickit-mockterm.h" #include #define streq(a,b) (!strcmp(a,b)) static char *lognames[] = { [EXPECT_GOTO] = "goto", [EXPECT_PRINT] = "print", [EXPECT_ERASECH] = "erasech", [EXPECT_CLEAR] = "clear", [EXPECT_SCROLLRECT] = "scrollrect", [EXPECT_SETPEN] = "setpen", }; static TickitMockTerm *mt; TickitTerm *make_term(int lines, int cols) { if(!mt) mt = tickit_mockterm_new(lines, cols); return (TickitTerm *)mt; } void drain_termlog(void) { tickit_mockterm_clearlog(mt); } void is_termlog(char *name, ...) { va_list args; va_start(args, name); int loglen = tickit_mockterm_loglen(mt); int logi; for(logi = 0; logi < loglen; logi++) { struct TermLogExpectation *exp = va_arg(args, void *); if(!exp) break; TickitMockTermLogEntry *got = tickit_mockterm_peeklog(mt, logi); if((int)exp->type != (int)got->type) { fail(name); diag("Expected %s(), got %s()", lognames[exp->type], lognames[got->type]); goto failed; } switch(exp->type) { case EXPECT_GOTO: case EXPECT_ERASECH: if(exp->val[0] == got->val1 && exp->val[1] == got->val2) continue; fail(name); diag("Expected %s(%d,%d), got %s(%d,%d)", lognames[exp->type], exp->val[0], exp->val[1], lognames[got->type], got->val1, got->val2); goto failed; case EXPECT_PRINT: if(streq(exp->str, got->str)) continue; fail(name); diag("Expected print(\"%s\"), got print(\"%s\")", exp->str, got->str); goto failed; case EXPECT_CLEAR: continue; case EXPECT_SCROLLRECT: if(exp->val[0] == got->rect.top && exp->val[1] == got->rect.left && exp->val[2] == got->rect.lines && exp->val[3] == got->rect.cols && exp->val[4] == got->val1 && exp->val[5] == got->val2) continue; fail(name); diag("Expected scrollrect(%d,%d,%d,%d, %+d,%+d), got scrollrect(%d,%d,%d,%d, %+d,%+d)", exp->val[0], exp->val[1], exp->val[2], exp->val[3], exp->val[4], exp->val[5], got->rect.top, got->rect.left, got->rect.lines, got->rect.cols, got->val1, got->val2); goto failed; case EXPECT_SETPEN: { int gotval; int expval; // Colours are tricky; -1 is the unspecified default if((expval = exp->fg == 0 ? -1 : exp->fg == EXPECT_ZERO ? 0 : exp->fg) != (gotval = tickit_pen_get_colour_attr(got->pen, TICKIT_PEN_FG))) { fail(name); diag("Expected setpen(fg=%d,...), got setpen(fg=%d,...)", expval, gotval); goto failed; } if((expval = exp->bg == 0 ? -1 : exp->bg == EXPECT_ZERO ? 0 : exp->bg) != (gotval = tickit_pen_get_colour_attr(got->pen, TICKIT_PEN_BG))) { fail(name); diag("Expected setpen(bg=%d,...), got setpen(bg=%d,...)", expval, gotval); goto failed; } // TODO: add remaining attributes } break; } } if(logi < loglen) { TickitMockTermLogEntry *got = tickit_mockterm_peeklog(mt, logi); fail(name); diag("Expected no more termlog entries; got %s()", lognames[got->type]); goto failed; } struct TermLogExpectation *exp; if((exp = va_arg(args, void *))) { fail(name); diag("Expected a %s() termlog entry; got none", lognames[exp->type]); goto failed; } pass(name); failed: tickit_mockterm_clearlog(mt); } void press_key(int type, char *str, int mod) { TickitKeyEventInfo info = { .type = type, .mod = mod, .str = str, }; tickit_term_emit_key((TickitTerm *)mt, &info); } void press_mouse(int type, int button, int line, int col, int mod) { TickitMouseEventInfo info = { .type = type, .button = button, .line = line, .col = col, .mod = mod, }; tickit_term_emit_mouse((TickitTerm *)mt, &info); } libtickit-0.3.4/t/taplib-mockterm.h0000644000000000000000000000247513613430267015345 0ustar 00000000000000#include struct TermLogExpectation { enum { EXPECT_GOTO = LOG_GOTO, EXPECT_PRINT = LOG_PRINT, EXPECT_ERASECH = LOG_ERASECH, EXPECT_CLEAR = LOG_CLEAR, EXPECT_SCROLLRECT = LOG_SCROLLRECT, EXPECT_SETPEN = LOG_SETPEN, } type; int val[6]; char *str; // These must match pen attr names int fg, bg, b, u, i, rv, strike, af; }; #define GOTO(line,col) \ &(struct TermLogExpectation){EXPECT_GOTO, .val = {line, col}} #define PRINT(s) \ &(struct TermLogExpectation){EXPECT_PRINT, .str = s} #define ERASECH(count,moveend) \ &(struct TermLogExpectation){EXPECT_ERASECH, .val = {count, moveend}} #define CLEAR() \ &(struct TermLogExpectation){EXPECT_CLEAR} #define SCROLLRECT(top,left,lines,cols,downward,rightward) \ &(struct TermLogExpectation){EXPECT_SCROLLRECT, .val = {top, left, lines, cols, downward, rightward}} // IMPORTANT: set an expectation of this to expect the value 0 explicitly; // 0 will be treated as unspecified #define EXPECT_ZERO 0x100000 #define SETPEN(...) \ &(struct TermLogExpectation){EXPECT_SETPEN, __VA_ARGS__} TickitTerm *make_term(int lines, int cols); void drain_termlog(void); void is_termlog(char *name, ...); void press_key(int type, char *str, int mod); void press_mouse(int type, int button, int line, int col, int mod); libtickit-0.3.4/t/taplib-tickit.c0000644000000000000000000000175113613430267015002 0ustar 00000000000000#include "taplib-tickit.h" #include "taplib.h" #include TickitRect *rect_init_strp(TickitRect *rect, const char *str) { int top, left, bottom, right, lines, cols; if(sscanf(str, "%d,%d..%d,%d", &left, &top, &right, &bottom) == 4) { tickit_rect_init_bounded(rect, top, left, bottom, right); return rect; } if(sscanf(str, "%d,%d+%d,%d", &left, &top, &cols, &lines) == 4) { tickit_rect_init_sized(rect, top, left, lines, cols); return rect; } return NULL; } void is_rect(TickitRect *got, const char *expect, char *name) { TickitRect exp; rect_init_strp(&exp, expect); if(got->top != exp.top || got->left != exp.left || got->lines != exp.lines || got->cols != exp.cols) { fail(name); diag("got %d,%d..%d,%d expected %d,%d..%d,%d", got->left, got->top, tickit_rect_right(got), tickit_rect_bottom(got), exp.left, exp.top, tickit_rect_right(&exp), tickit_rect_bottom(&exp)); } else { pass(name); } } libtickit-0.3.4/t/taplib-tickit.h0000644000000000000000000000032513613430267015003 0ustar 00000000000000#ifndef __TAPLIB_TICKIT_H__ #define __TAPLIB_TICKIT_H__ #include "tickit.h" TickitRect *rect_init_strp(TickitRect *rect, const char *str); void is_rect(TickitRect *got, const char *expect, char *name); #endif libtickit-0.3.4/t/taplib.c0000644000000000000000000000472713613430267013523 0ustar 00000000000000#include "taplib.h" #include #include #include #include static int plan_printed = 0; static int nexttest = 1; static int _exit_status = 0; void plan_tests(int n) { printf("1..%d\n", n); plan_printed = 1; } void skip_all(char *reason) { printf("1..0 # SKIP %s\n", reason); plan_printed = 1; } void pass(char *name) { printf("ok %d - %s\n", nexttest++, name); } void fail(char *name) { printf("not ok %d - %s\n", nexttest++, name); _exit_status = 1; } void ok(int cmp, char *name) { if(cmp) pass(name); else fail(name); } void diag(char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "# "); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); } void is_int(int got, int expect, char *name) { if(got == expect) ok(1, name); else { ok(0, name); diag("got %d expected %d", got, expect); } } void is_ptr(void *got, void *expect, char *name) { if(got == expect) ok(1, name); else { ok(0, name); diag("got %p expected %p", got, expect); } } void is_str(const char *got, const char *expect, char *name) { if(strcmp(got, expect) == 0) ok(1, name); else { ok(0, name); diag("got '%s' expected '%s'", got, expect); } } static char *strescape(const char *s) { size_t len = 0; char *ret; char *q; for(const char *p = s; p[0]; p++) switch(p[0]) { case '\b': case '\n': case '\r': case '\x1b': case '\\': len += 2; break; default: len += 1; } ret = malloc(len + 1); q = ret; for(const char *p = s; p[0]; p++) switch(p[0]) { case '\b': q[0] = '\\'; q[1] = 'b'; q += 2; break; case '\n': q[0] = '\\'; q[1] = 'n'; q += 2; break; case '\r': q[0] = '\\'; q[1] = 'r'; q += 2; break; case '\x1b': q[0] = '\\'; q[1] = 'e'; q += 2; break; case '\\': q[0] = '\\'; q[1] = '\\'; q += 2; break; default: q[0] = p[0]; q += 1; break; } q[0] = 0; return ret; } void is_str_escape(const char *got, const char *expect, char *name) { if(strcmp(got, expect) == 0) ok(1, name); else { char *got_e = strescape(got); char *expect_e = strescape(expect); ok(0, name); diag("got \"%s\" expected \"%s\"", got_e, expect_e); free(got_e); free(expect_e); } } int exit_status(void) { if(!plan_printed) printf("1..%d\n", nexttest-1); return _exit_status; } libtickit-0.3.4/t/taplib.h0000644000000000000000000000062613613430267013522 0ustar 00000000000000void plan_tests(int n); void skip_all(char *reason); void ok(int cmp, char *name); void pass(char *name); void fail(char *name); void diag(char *fmt, ...); void is_int(int got, int expect, char *name); void is_ptr(void *got, void *expect, char *name); void is_str(const char *got, const char *expect, char *name); void is_str_escape(const char *got, const char *expect, char *name); int exit_status(void);