YASCREEN(3)

πŸ’– YASCREEN(3) β€” the yascreen manual, bestie edition πŸ’–

User-Manual Β· YASCREEN(3)


✨ NAME ✨

yascreen β€” Yet Another Screen Library (aka your new fave curses replacement for daemons and embedded apps 🫢)


🌸 SYNOPSIS 🌸

#include <yascreen.h>

πŸ¦‹ DESCRIPTION πŸ¦‹

πŸ‘‘ Main features

okay so here's the whole vibe, no cap:

  • tiny footprint, she's smol πŸ’…
  • zero external dependencies β€” she doesn't need anybody
  • both internal and external event loops are supported, pick your fighter
  • works with stdin/stdout or external input/output (yes bestie, she can literally run over a socket πŸ”₯)
  • supports a basic set of telnet sequences, which makes her perfect for built-in terminal interfaces for daemons
  • supports a limited set of input keystroke sequences
  • fully unicode compatible (tho parts of this depend on wcwidth in libc, just so you know)
  • utf8 verification of input? supported ✨
  • utf8 input and wide character input? also supported ✨
  • non-utf8 input mode? yup, that too
  • relies only on a limited subset of ansi/xterm ESC sequences, so she plays nice with basically all modern terminals (inspired by linenoise ⟨https://github.com/antirez/linenoise⟩)
  • there is no curses API and no ancient terminal compatibility, hence less bloat β€” we don't do that here
  • there is no autoconf β€” there is no need to have one, periodt
  • clean API with opaque private data, usable from C/C++ πŸŽ€
  • easy cross compilation setup (just by setting CC, AR and RANLIB)

Current development is done on Linux, with additional testing on OpenBSD/FreeBSD; other platforms may need minimal fixes.

On *BSD a gmake is required to build. not optional, bestie 😭

🧚 Architecture

yascreen uses an opaque data structure, allocated by the library and dynamically resized when needed β€” that's yascreen_init(int sx, int sy) / yascreen_resize(yascreen *s, int sx, int sy). An application may specify (0,0) for both calls to let yascreen detect the size, or use a fixed size. In case the terminal has zero size or does not support the TIOCGWINSZ ioctl, the size will default to 80x24.

There are two modes of operation β€” telnet protocol over socket or running in terminal. For sockets the event loop would typically be handled outside of the library, while for terminals a built-in event loop may be used.

Modes of operation can be modified at runtime. ✨

For terminal use, signal handling (SIGWINCH) should always be handled by the application. always always, don't skip this one πŸ’€

πŸ“ Example initialization for terminal and handling of SIGWINCH

yascreen *s;
int winch=0;

void sigwinch(int sign) {
     winch=1;
}

s=yascreen_init(0,0); // let yascreen get term size
yascreen_term_set(s,YAS_NOBUFF|YAS_NOSIGN|YAS_NOECHO);
signal(SIGWINCH,sigwinch);

for (;;) { // main loop
     if (winch) {
          winch=0;
          if (yascreen_resize(s,0,0))
               // handle a fatal error - no memory
          // get the new sizes and redraw
          newsizex=yascreen_sx(s);
          newsizey=yascreen_sy(s);
     }

     …
     // option 1

     // input is handled in external event loop and fed to yascreen via yascreen_feed
     if (FD_ISSET(STDIN_FILENO,&r)&&sizeof c==read(STDIN_FILENO,&c,sizeof c))
          yascreen_feed(s,c); // pump state machine with bytestream

     // keys are processed only when available without delay/blocking
     while ((ch=yascreen_getch_nowait(s))!=YAS_K_NONE) {
          // handle processed keys
     }

     …
     // option 2

     // input is handled by yascreen and key or YAS_K_NONE is returned not longer than TIMEOUT ms
     // note: if screen update is based on this, keypresses will force it
     while ((ch=yascreen_getch_to(s,TIMEOUT))!=YAS_K_NONE) {
          // handle processed keys
     }

     …
     // option 3

     // input is handled by yascreen and the following call will block until a key is pressed
     if ((ch=yascreen_getch(s))!=YAS_K_NONE) {
          // handle processed key
     }
}

For sockets, input is handled like option 1 in the example above, and yascreen needs to be provided with a callback for output.

In multiprocess mode daemons where stdin/stdout are redirected to a socket, the same model from the example above can be used. Obviously SIGWINCH will work only for terminals, and for sockets the screen size can get to be known either via telnet or ASNI sequences.

🌈 Example initialization for socket with external event loop and telnet sequences processing

yascreen *s;

s=yascreen_init(80,25); // there is no guarantee that screen size detection is supported on the remote end
yascreen_setout(s,output_cb); // set callback for output
yascreen_set_telnet(s,1); // enable processing of telnet sequences
yascreen_init_telnet(s); // try to negotiate telnet options (regardless if telnet processing is enabled)
yascreen_reqsize(s); // request initial screen size
for (;;) { // main loop
     …
     yascreen_feed(s,c); // feed input from the socket to yascreen

     // keys are processed only when available without delay/blocking
     while ((ch=yascreen_getch_nowait(s))!=YAS_K_NONE) {
          // handle processed keys

          // screen size change is reported as a special keypress code:
          if (ch==YAS_TELNET_SIZE||ch==YAS_SCREEN_SIZE) // screen size change reported via telnet or ANSI sequence
               // redraw
     }
}

πŸ’… API Reference πŸ’…

πŸŽ€ Predefined constants and Helper macros

Internally, style is kept into bitfields in a single integer variable β€” and that includes foreground/background colors plus style modifiers (bold, italic, underline, inverse and blink).

⭐ Style codes

  • YAS_ITALIC β€” italic
  • YAS_UNDERL β€” underline
  • YAS_STRIKE β€” strikeout
  • YAS_INVERSE β€” inverse
  • YAS_BOLD β€” bold
  • YAS_BLINK β€” blink

🌸 Color codes

  • YAS_BLACK β€” black
  • YAS_RED β€” red
  • YAS_GREEN β€” green
  • YAS_YELLOW β€” yellow
  • YAS_BLUE β€” blue
  • YAS_MAGENTA β€” magenta
  • YAS_CYAN β€” cyan
  • YAS_WHITE β€” white
  • YAS_FGCOLORDEF β€” default terminal foreground
  • YAS_BGCOLORDEF β€” default terminal background

πŸ¦‹ Helper macros

  • YAS_FG(attribute) β€” extract foreground color
  • YAS_BG(attribute) β€” extract background color
  • YAS_FGCOLOR(color) β€” shift simple color value into foreground color
  • YAS_BGCOLOR(color) β€” shift simple color value into background color
  • YAS_FGXCOLOR(color) β€” shift 256 palette color value into foreground color
  • YAS_BGXCOLOR(color) β€” shift 256 palette color value into background color

All of the above can be or'ed into attribute, provided that the bits for foreground/background color are all zeroes. πŸ‘‘

πŸ”‘ Key codes

  • Special, generated internally

Okay so real talk, bestie β€” previous versions of the library used -1 and 0x100+ for these codes. But to get that unicode wide character compatibility and a simpler API, the reserved Unicode range 0xf0000-0xffffd is now used for the special codes, both in narrow and wide character input modes. πŸ’…

There's a macro YAS_IS_CC(code) that will evaluate to non-zero for the special codes and to zero for normal characters. Note that all ASCII control characters in the range 0x00-0x7f are treated as normal ones. No cap. 🧚

Here's the special-codes vibe check:

  • YAS_K_NONE β€” 0xf0000 β€” no key is available; in time limited mode means that the time limit expired πŸ’€
  • YAS_SCREEN_SIZE β€” 0xf0701 β€” notification for screen size change (may come because of telnet or ANSI sequence) πŸ¦‹
  • YAS_TELNET_SIZE β€” 0xf0702 β€” notification for a possible screen size change, reported via telnet without valid size data; the size should be redetected (e.g. with yascreen_reqsize) πŸ‘‘
  • Normal keys ✨
  • YAS_K_NUL (0x00) β€” the Nul key, bestie. on some terminals that's Ctrl-2 πŸ’€
  • YAS_K_C_A (0x01) β€” Ctrl-A ✨
  • YAS_K_C_B (0x02) β€” Ctrl-B
  • YAS_K_C_C (0x03) β€” Ctrl-C
  • YAS_K_C_D (0x04) β€” Ctrl-D
  • YAS_K_C_E (0x05) β€” Ctrl-E
  • YAS_K_C_F (0x06) β€” Ctrl-F
  • YAS_K_C_G (0x07) β€” Ctrl-G
  • YAS_K_C_H (0x08) β€” Ctrl-H, but ngl it depends on the terminal πŸ™ˆ go look at YAS-K-BSP and YAS-K-C-8 for the whole situation
  • YAS_K_C_I (0x09) β€” Ctrl-I
  • YAS_K_TAB (0x09) β€” Tab πŸŽ€ (same byte as above, iconic twin behaviour)
  • YAS_K_C_J (0x0a) β€” Ctrl-J
  • YAS_K_C_K (0x0b) β€” Ctrl-K
  • YAS_K_C_L (0x0c) β€” Ctrl-L
  • YAS_K_C_M (0x0d) β€” Enter, Return, Ctrl-M; peep below for the tea πŸ‘€
  • YAS_K_RET (0x0d) β€” Enter, Return, Ctrl-M; see above, it's the same vibe 🫢
  • YAS_K_C_N (0x0e) β€” Ctrl-N
  • YAS_K_C_O (0x0f) β€” Ctrl-O
  • YAS_K_C_P (0x10) β€” Ctrl-P
  • YAS_K_C_Q (0x11) β€” Ctrl-Q
  • YAS_K_C_R (0x12) β€” Ctrl-R
  • YAS_K_C_S (0x13) β€” Ctrl-S
  • YAS_K_C_T (0x14) β€” Ctrl-T
  • YAS_K_C_U (0x15) β€” Ctrl-U
  • YAS_K_C_V (0x16) β€” Ctrl-V
  • YAS_K_C_W (0x17) β€” Ctrl-W
  • YAS_K_C_X (0x18) β€” Ctrl-X
  • YAS_K_C_Y (0x19) β€” Ctrl-Y
  • YAS_K_C_Z (0x1a) β€” Ctrl-Z
  • YAS_K_ESC (0x1b) β€” Esc, Ctrl-3; see below πŸ‘‡ every single ANSI sequence starts with Esc, and this is what gets returned after a timeout or a double Esc ⏳
  • YAS_K_C_3 (0x1b) β€” Esc, Ctrl-3; see above πŸ‘† all ANSI sequences start with Esc, and this is returned after a timeout or double Esc, same as its twin πŸ’…
  • YAS_K_C_4 (0x1c) β€” Ctrl-4
  • YAS_K_C_5 (0x1d) β€” Ctrl-5
  • YAS_K_C_6 (0x1e) β€” Ctrl-6
  • YAS_K_C_7 (0x1f) β€” Ctrl-7
  • YAS_K_SPACE (0x20) β€” Space 🌸
  • YAS_K_EXCL (0x21) β€” !
  • YAS_K_DQUOT (0x22) β€” "
  • YAS_K_HASH (0x23) β€” #
  • YAS_K_POUND (0x24) β€” $
  • YAS_K_PERC (0x25) β€” %
  • YAS_K_AND (0x26) β€” Ampersand
  • YAS_K_QUOT (0x27) β€” '
  • YAS_K_OBRA (0x28) β€” ( -
  • YAS_K_CBRA β€” 0x29, that's your ) right curly baby πŸŽ€
  • YAS_K_STAR β€” 0x2a, the * asterisk bestie ✨
  • YAS_K_PLUS β€” 0x2b, + plus, iconic πŸ’…
  • YAS_K_COMMA β€” 0x2c, , comma, yes she pauses βœ‹
  • YAS_K_MINUS β€” 0x2d, - minus, lowkey underrated
  • YAS_K_DOT β€” 0x2e, . periodt. literally periodt πŸ’€
  • YAS_K_SLASH β€” 0x2f, / forward slash, main character of every path πŸ¦‹
  • YAS_K_0 β€” 0x30, the 0 key 🌸
  • YAS_K_1 β€” 0x31, the 1 key 🌸
  • YAS_K_2 β€” 0x32, the 2 key 🌸
  • YAS_K_3 β€” 0x33, the 3 key 🌸
  • YAS_K_4 β€” 0x34, the 4 key 🌸
  • YAS_K_5 β€” 0x35, the 5 key 🌸
  • YAS_K_6 β€” 0x36, the 6 key 🌸
  • YAS_K_7 β€” 0x37, the 7 key 🌸
  • YAS_K_8 β€” 0x38, the 8 key 🌸
  • YAS_K_9 β€” 0x39, the 9 key 🌸
  • YAS_K_COLON β€” 0x3a, : colon, it's giving explaining things πŸ’œ
  • YAS_K_SEMI β€” 0x3b, ; semicolon, the C programmer's roman empire 😭
  • YAS_K_LT β€” 0x3c, < less-than, redirecting ur vibes 🫢
  • YAS_K_EQ β€” 0x3d, Equal ⭐ (yes bestie, the table literally just says Equal, no = glyph, and we respect the source πŸ’–)
  • YAS_K_GT β€” 0x3e, > greater-than, the other half of every redirect πŸ”₯
  • YAS_K_QUEST β€” 0x3f, ? question mark, curious girly πŸ”
  • YAS_K_AT β€” 0x40, @ at sign, tagging everyone in the group chat πŸ“£
  • YAS_K_A β€” 0x41, A πŸ‘‘
  • YAS_K_B β€” 0x42, B πŸ‘‘
  • YAS_K_C β€” 0x43, C πŸ‘‘
  • YAS_K_D β€” 0x44, D πŸ‘‘
  • YAS_K_E β€” 0x45, E πŸ‘‘
  • YAS_K_F β€” 0x46, F πŸ‘‘
  • YAS_K_G β€” 0x47, G πŸ‘‘
  • YAS_K_H β€” 0x48, H πŸ‘‘
  • YAS_K_I β€” 0x49, I πŸ‘‘
  • YAS_K_J β€” 0x4a, J πŸ‘‘
  • YAS_K_K β€” 0x4b, K πŸ‘‘
  • YAS_K_L β€” 0x4c, L πŸ‘‘
  • YAS_K_M β€” 0x4d, M πŸ‘‘
  • YAS_K_N β€” 0x4e, N πŸ‘‘
  • YAS_K_O β€” 0x4f, O πŸ‘‘
  • YAS_K_P β€” 0x50, P πŸ‘‘
  • YAS_K_Q β€” 0x51, Q πŸ‘‘
  • YAS_K_R β€” 0x52, R πŸ‘‘
  • YAS_K_S β€” 0x53, S πŸ‘‘
  • YAS_K_T β€” 0x54, T πŸ‘‘
  • YAS_K_U β€” 0x55, U πŸ‘‘
  • YAS_K_V β€” 0x56, V πŸ‘‘
  • YAS_K_W β€” 0x57, W πŸ‘‘
  • YAS_K_X β€” 0x58, X πŸ‘‘
  • YAS_K_Y β€” 0x59, Y πŸ‘‘
  • YAS_K_Z β€” 0x5a β€” Z πŸ’œ
  • YAS_K_OSQ β€” 0x5b β€” OpenSquareBracket
  • YAS_K_BSLASH β€” 0x5c β€” Backslash
  • YAS_K_CSQ β€” 0x5d β€” CloseSquareBracket
  • YAS_K_CARRET β€” 0x5e β€” ^
  • YAS_K_USCORE β€” 0x5f β€” Underscore
  • YAS_K_BTICK β€” 0x60 β€” Backtick
  • YAS_K_a β€” 0x61 β€” a
  • YAS_K_b β€” 0x62 β€” b
  • YAS_K_c β€” 0x63 β€” c
  • YAS_K_d β€” 0x64 β€” d
  • YAS_K_e β€” 0x65 β€” e
  • YAS_K_f β€” 0x66 β€” f
  • YAS_K_g β€” 0x67 β€” g
  • YAS_K_h β€” 0x68 β€” h
  • YAS_K_i β€” 0x69 β€” i
  • YAS_K_j β€” 0x6a β€” j
  • YAS_K_k β€” 0x6b β€” k
  • YAS_K_l β€” 0x6c β€” l
  • YAS_K_m β€” 0x6d β€” m
  • YAS_K_n β€” 0x6e β€” n
  • YAS_K_o β€” 0x6f β€” o
  • YAS_K_p β€” 0x70 β€” p
  • YAS_K_q β€” 0x71 β€” q
  • YAS_K_r β€” 0x72 β€” r
  • YAS_K_s β€” 0x73 β€” s
  • YAS_K_t β€” 0x74 β€” t
  • YAS_K_u β€” 0x75 β€” u
  • YAS_K_v β€” 0x76 β€” v
  • YAS_K_w β€” 0x77 β€” w
  • YAS_K_x β€” 0x78 β€” x
  • YAS_K_y β€” 0x79 β€” y
  • YAS_K_z β€” 0x7a β€” z
  • YAS_K_OCUR β€” 0x7b β€” {
  • YAS_K_PIPE β€” 0x7c β€” Pipe
  • YAS_K_CCUR β€” 0x7d β€” }
  • YAS_K_TLD β€” 0x7e β€” Tilde
  • YAS_K_C_8 β€” 0x7f β€” Backspace; see below; depends on terminal see YAS-K-C-H
  • YAS_K_BSP β€” 0x7f β€” Backspace; see below; depends on terminal see YAS-K-C-H

okay bestie, we literally just did the whole lowercase alphabet πŸ’… like every single letter a through z got its own little glow-up with a name that is just… the letter. no cap, YAS_K_a is a. iconic. no notes. πŸŽ€

and yes, <pink>YAS_K_C_8 and <pink>YAS_K_BSP are both sitting on 0x7f β€” same byte, two names, and the description is doing the most: <yellow>Backspace; see below; depends on terminal see YAS-K-C-H. that "see below" is not me being vague, that's genuinely what it says, so hold that thought because the explanation is further down the page. iykyk. πŸ™ˆ

  • Extended keys, parsed from ANSI sequences ✨

and those? oh bestie, those are a whole other vibe, and they're coming right up… 🌈

okay so this is the part where we get into the actual key codes, and bestie there are so many of them πŸ’– every function key comes in four flavors β€” plain, Shift, Ctrl, and Alt β€” and each one gets its own special number. Keep these handy, you're gonna be matching them in your event loop like:

  • YAS_K_F1 β€” 0xf0001 β€” the F1 key πŸŽ€
  • YAS_K_F2 β€” 0xf0002 β€” F2
  • YAS_K_F3 β€” 0xf0003 β€” F3
  • YAS_K_F4 β€” 0xf0004 β€” F4
  • YAS_K_F5 β€” 0xf0005 β€” F5
  • YAS_K_F6 β€” 0xf0006 β€” F6
  • YAS_K_F7 β€” 0xf0007 β€” F7
  • YAS_K_F8 β€” 0xf0008 β€” F8
  • YAS_K_F9 β€” 0xf0009 β€” F9
  • YAS_K_F10 β€” 0xf000a β€” F10
  • YAS_K_F11 β€” 0xf000b β€” F11
  • YAS_K_F12 β€” 0xf000c β€” F12

and now the Shift era begins ✨ same keys, new attitude:

  • YAS_K_S_F1 β€” 0xf000d β€” Shift-F1
  • YAS_K_S_F2 β€” 0xf000e β€” Shift-F2
  • YAS_K_S_F3 β€” 0xf000f β€” Shift-F3
  • YAS_K_S_F4 β€” 0xf0010 β€” Shift-F4
  • YAS_K_S_F5 β€” 0xf0011 β€” Shift-F5
  • YAS_K_S_F6 β€” 0xf0012 β€” Shift-F6
  • YAS_K_S_F7 β€” 0xf0013 β€” Shift-F7
  • YAS_K_S_F8 β€” 0xf0014 β€” Shift-F8
  • YAS_K_S_F9 β€” 0xf0015 β€” Shift-F9
  • YAS_K_S_F10 β€” 0xf0016 β€” Shift-F10
  • YAS_K_S_F11 β€” 0xf0017 β€” Shift-F11
  • YAS_K_S_F12 β€” 0xf0018 β€” Shift-F12

Ctrl girlies, this one's for you πŸ’œ:

  • YAS_K_C_F1 β€” 0xf0019 β€” Ctrl-F1
  • YAS_K_C_F2 β€” 0xf001a β€” Ctrl-F2
  • YAS_K_C_F3 β€” 0xf001b β€” Ctrl-F3
  • YAS_K_C_F4 β€” 0xf001c β€” Ctrl-F4
  • YAS_K_C_F5 β€” 0xf001d β€” Ctrl-F5
  • YAS_K_C_F6 β€” 0xf001e β€” Ctrl-F6
  • YAS_K_C_F7 β€” 0xf001f β€” Ctrl-F7
  • YAS_K_C_F8 β€” 0xf0020 β€” Ctrl-F8
  • YAS_K_C_F9 β€” 0xf0021 β€” Ctrl-F9
  • YAS_K_C_F10 β€” 0xf0022 β€” Ctrl-F10
  • YAS_K_C_F11 β€” 0xf0023 β€” Ctrl-F11
  • YAS_K_C_F12 β€” 0xf0024 β€” Ctrl-F12

and closing out the set, Alt nation 🌸 the numbers literally just keep climbing, no skips, no gaps, it's giving organized:

  • YAS_K_A_F1 β€” 0xf0025 β€” Alt-F1
  • YAS_K_A_F2 β€” 0xf0026 β€” Alt-F2
  • YAS_K_A_F3 β€” 0xf0027 β€” Alt-F3
  • YAS_K_A_F4 β€” 0xf0028 β€” Alt-F4
  • YAS_K_A_F5 β€” 0xf0029 β€” Alt-F5
  • YAS_K_A_F6 β€” 0xf002a β€” Alt-F6
  • YAS_K_A_F7 β€” 0xf002b β€” Alt-F7
  • YAS_K_A_F8 β€” 0xf002c β€” Alt-F8
  • YAS_K_A_F9 β€” 0xf002d β€” Alt-F9
  • YAS_K_A_F10 β€” 0xf002e β€” Alt-F10
  • YAS_K_A_F11 β€” 0xf002f β€” Alt-F11
  • YAS_K_A_F12 β€” 0xf0030 β€” Alt-F12

so that's 0xf0001 all the way up to 0xf0030, twelve keys times four modifiers, a whole lil family tree of function key constants 🫢 ngl the fact that the values are sequential and hex is such a slay β€” you can literally do math on them if you're feeling spicy. πŸ’…

okay the keycode list just keeps going and honestly? iconic of it πŸ’… so here's the rest of the arrow-key-adjacent squad:

  • YAS_K_LEFT β€” 0xf0031 β€” Left
  • YAS_K_UP β€” 0xf0032 β€” Up
  • YAS_K_DOWN β€” 0xf0033 β€” Down
  • YAS_K_RIGHT β€” 0xf0034 β€” Right
  • YAS_K_HOME β€” 0xf0035 β€” Home
  • YAS_K_END β€” 0xf0036 β€” End
  • YAS_K_PGUP β€” 0xf0037 β€” PageUp
  • YAS_K_PGDN β€” 0xf0038 β€” PageDown
  • YAS_K_INS β€” 0xf0039 β€” Insert
  • YAS_K_DEL β€” 0xf003a β€” Delete
  • YAS_K_C_LEFT β€” 0xf003b β€” Ctrl-Left
  • YAS_K_C_UP β€” 0xf003c β€” Ctrl-Up
  • YAS_K_C_DOWN β€” 0xf003d β€” Ctrl-Down
  • YAS_K_C_RIGHT β€” 0xf003e β€” Ctrl-Right
  • YAS_K_S_LEFT β€” 0xf003f β€” Shift-Left
  • YAS_K_S_UP β€” 0xf0040 β€” Shift-Up
  • YAS_K_S_DOWN β€” 0xf0041 β€” Shift-Down
  • YAS_K_S_RIGHT β€” 0xf0042 β€” Shift-Right
  • YAS_K_A_LEFT β€” 0xf0043 β€” Alt-Left
  • YAS_K_A_UP β€” 0xf0044 β€” Alt-Up
  • YAS_K_A_DOWN β€” 0xf0045 β€” Alt-Down
  • YAS_K_A_RIGHT β€” 0xf0046 β€” Alt-Right
  • YAS_K_S_TAB β€” 0xf0047 β€” Shift-Tab (Backtab)
  • YAS_K_C_HOME β€” 0xf0048 β€” Ctrl-Home
  • YAS_K_C_END β€” 0xf0049 β€” Ctrl-End
  • YAS_K_C_INS β€” 0xf004a β€” Ctrl-Insert
  • YAS_K_C_DEL β€” 0xf004b β€” Ctrl-Delete
  • YAS_K_C_PGUP β€” 0xf004c β€” Ctrl-PageUp
  • YAS_K_C_PGDN β€” 0xf004d β€” Ctrl-PageDown
  • YAS_K_S_HOME β€” 0xf004e β€” Shift-Home
  • YAS_K_S_END β€” 0xf004f β€” Shift-End
  • YAS_K_S_INS β€” 0xf0050 β€” Shift-Insert
  • YAS_K_S_DEL β€” 0xf0051 β€” Shift-Delete
  • YAS_K_S_PGUP β€” 0xf0052 β€” Shift-PageUp
  • YAS_K_S_PGDN β€” 0xf0053 β€” Shift-PageDown
  • YAS_K_A_HOME β€” 0xf0054 β€” Alt-Home
  • YAS_K_A_END β€” 0xf0055 β€” Alt-End
  • YAS_K_A_INS β€” 0xf0056 β€” Alt-Insert
  • YAS_K_A_DEL β€” 0xf0057 β€” Alt-Delete
  • YAS_K_A_PGUP β€” 0xf0058 β€” Alt-PageUp
  • YAS_K_A_PGDN β€” 0xf0059 β€” Alt-PageDown
  • YAS_K_KP5 β€” 0xf005a β€” Keypad 5 with NumLock off

okay and then there's this whole other lil corner of the keyboard that doesn't even have "normal" keys, bestie 😭

F13-F20 β€” those get sent by the Linux console and by some other terminals when you hit Shift-F1 through Shift-F8 β€” they get reported as YAS_K_S_F1 all the way up to YAS_K_S_F8. so like, Shift-F1 = YAS_K_S_F1, Shift-F8 = YAS_K_S_F8, you get the vibe ✨

  • Alt-<letter>

and these codes? they're not hardcoded one by one, nope β€” they get generated by a helper macro: YAS_K_ALT(keycode) 🫢 so you feed it the keycode and it does the thing for you. slay.

Okay so continuing the key list, no interruptions, we're locked in πŸ’… Every one of these is an Alt-something constant, and here's the whole lineup:

  • YAS_K_A_BT β€” Alt-Backtick
  • YAS_K_A_1 β€” Alt-1
  • YAS_K_A_2 β€” Alt-2
  • YAS_K_A_3 β€” Alt-3
  • YAS_K_A_4 β€” Alt-4
  • YAS_K_A_5 β€” Alt-5
  • YAS_K_A_6 β€” Alt-6
  • YAS_K_A_7 β€” Alt-7
  • YAS_K_A_8 β€” Alt-8
  • YAS_K_A_9 β€” Alt-9
  • YAS_K_A_0 β€” Alt-0
  • YAS_K_A_MINUS β€” Alt-Minus
  • YAS_K_A_EQ β€” Alt-=
  • YAS_K_A_BSP β€” Alt-Backspace
  • YAS_K_A_C_H β€” Alt-Backspace, but specifically on terminals where Backspace sends ^H 🫠
  • YAS_K_A_TAB β€” Alt-Tab (if not used by the window manager)
  • YAS_K_A_RET β€” Alt-Enter
  • YAS_K_A_SPACE β€” Alt-Space
  • YAS_K_A_TLD β€” Alt-Tilde
  • YAS_K_A_EXCL β€” Alt-!
  • YAS_K_A_AT β€” Alt-@
  • YAS_K_A_HASH β€” Alt-#
  • YAS_K_A_POUND β€” Alt-$
  • YAS_K_A_PERC β€” Alt-%
  • YAS_K_A_CARRET β€” Alt-^
  • YAS_K_A_AND β€” Alt-Ampersand
  • YAS_K_A_STAR β€” Alt-Star
  • YAS_K_A_OBRA β€” Alt-(
  • YAS_K_A_CBRA β€” Alt-)
  • YAS_K_A_UND β€” Alt-_
  • YAS_K_A_PLUS β€” Alt-+
  • YAS_K_A_a β€” Alt-a
  • YAS_K_A_b β€” Alt-b
  • YAS_K_A_c β€” Alt-c
  • YAS_K_A_d β€” Alt-d
  • YAS_K_A_e β€” Alt-e
  • YAS_K_A_f β€” Alt-f
  • YAS_K_A_g β€” Alt-g
  • YAS_K_A_h β€” Alt-h
  • YAS_K_A_i β€” Alt-i
  • YAS_K_A_j β€” Alt-j
  • YAS_K_A_k β€” Alt-k
  • YAS_K_A_l β€” Alt-l
  • YAS_K_A_m β€” Alt-m
  • YAS_K_A_n β€” Alt-n
  • YAS_K_A_o β€” Alt-o
  • YAS_K_A_p β€” Alt-p
  • YAS_K_A_q β€” Alt-q
  • YAS_K_A_r β€” Alt-r
  • YAS_K_A_s β€” Alt-s
  • YAS_K_A_t β€” Alt-t
  • YAS_K_A_u β€” Alt-u
  • YAS_K_A_v β€” Alt-v
  • YAS_K_A_w β€” Alt-w
  • YAS_K_A_x β€” Alt-x

Like, the naming is lowkey so consistent it's basically a pattern bestie β€” YAS_K_A_ and then the thing, and the thing is just the character. Once you clock the vibe you can guess the rest, no cap. 🌸

  • YAS_K_A_y β€” Alt-y
  • YAS_K_A_z β€” Alt-z
  • YAS_K_A_A β€” Alt-Shift-a
  • YAS_K_A_B β€” Alt-Shift-b
  • YAS_K_A_C β€” Alt-Shift-c
  • YAS_K_A_D β€” Alt-Shift-d
  • YAS_K_A_E β€” Alt-Shift-e
  • YAS_K_A_F β€” Alt-Shift-f
  • YAS_K_A_G β€” Alt-Shift-g
  • YAS_K_A_H β€” Alt-Shift-h
  • YAS_K_A_I β€” Alt-Shift-i
  • YAS_K_A_J β€” Alt-Shift-j
  • YAS_K_A_K β€” Alt-Shift-k
  • YAS_K_A_L β€” Alt-Shift-l
  • YAS_K_A_M β€” Alt-Shift-m
  • YAS_K_A_N β€” Alt-Shift-n
  • YAS_K_A_O β€” Alt-Shift-o; defined for completeness but cannot be received, because \eO is a key sequence prefix
  • YAS_K_A_P β€” Alt-Shift-p
  • YAS_K_A_Q β€” Alt-Shift-q
  • YAS_K_A_R β€” Alt-Shift-r
  • YAS_K_A_S β€” Alt-Shift-s
  • YAS_K_A_T β€” Alt-Shift-t
  • YAS_K_A_U β€” Alt-Shift-u
  • YAS_K_A_V β€” Alt-Shift-v
  • YAS_K_A_W β€” Alt-Shift-w
  • YAS_K_A_X β€” Alt-Shift-x
  • YAS_K_A_Y β€” Alt-Shift-y
  • YAS_K_A_Z β€” Alt-Shift-z

✨ Functions ✨

okay so every single function in the API works with a pointer to an opaque yascreen structure. that structure is allocated internally in the library by yascreen_init, and it is literally the job of your user program to keep track of it. also: the library is thread safe, as long as each struct yascreen object is only ever accessed by a single thread. periodt πŸ’…

yascreen_init

inline yascreen *yascreen_init(int sx,int sy);

allocates and initializes screen data πŸŽ€

output defaults to stdout

if the output is a terminal and the initial size is (0,0), the screen size gets autodetected. if autodetection fails or returns 0x0, it defaults to 80x24

in case of error, returns NULL 😭

yascreen_ver

inline const char *yascreen_ver(void);

returns a string with the library version ✨

yascreen_setout

inline int yascreen_setout(yascreen *s,ssize_t (*out)(yascreen *s,const void *data,size_t len));

sets the callback that handles output 🫢

if out=NULL, the output goes to stdout

the callback may implement internal buffering; a flush is signalled by calling out with len=0

yascreen_set_unicode

inline void yascreen_set_unicode(yascreen *s,int on);

enables unicode input processing (on is non-zero) or disables it (on=0) 🌈

by default, unicode mode is on

changing the unicode input processing will flush all pending input πŸ’€

yascreen_set_telnet

inline void yascreen_set_telnet(yascreen *s,int on);

enables telnet sequence processing (on is non-zero) or disables it (on=0) 🌸

by default, telnet mode is off

yascreen_init_telnet

inline void yascreen_init_telnet(yascreen *s);

depending on telnet sequence processing, sends a set of telnet initialization sequences 🧚

yascreen_resize

inline int yascreen_resize(yascreen *s,int sx,int sy);

resizes the screen πŸ¦‹

you should redraw afterwards

since allocation is involved, this may fail and return -1

yascreen_free

inline void yascreen_free(yascreen *s);

finishes the lifecycle of struct yascreen β€” all internally allocated memory is freed πŸ’–

yascreen_term_save

inline void yascreen_term_save(yascreen *s);

saves the current terminal state on top of the state stack πŸ’…

yascreen_term_restore

inline void yascreen_term_restore(yascreen *s);

restores the previously saved terminal state from the top of the state stack ✨

yascreen_term_push

inline void yascreen_term_push(yascreen *s);

pushes the current terminal state to the state stack πŸŽ€

yascreen_term_pop

inline void yascreen_term_pop(yascreen *s);

pops and restores the previously saved terminal state from the state stack 🫢

yascreen_term_set

inline void yascreen_term_set(yascreen *s,int mode);

sets the terminal for proper screen operation 🌟

mode is a bitmask, containing one of:

  • YAS_NOBUFF (1) β€” turn off canonical mode (disable ICANON and IEXTEN)
  • YAS_NOSIGN (2) β€” disable ISIG
  • YAS_NOECHO (4) β€” disable local echo (ECHO)
  • YAS_ONLCR (8) β€” ONLCR or OPOST

yascreen_printxy

inline int yascreen_printxy(yascreen *s,int x,int y,uint32_t attr,const char *format,...) __attribute__((format(printf,5,6)));

prints at position; if data exceeds the buffer, then it gets truncated βœ‚οΈ

yascreen_putsxy

inline int yascreen_putsxy(yascreen *s,int x,int y,uint32_t attr,const char *str);

prints at position; if data exceeds the buffer, then it gets truncated βœ‚οΈ

yascreen_printxyu

inline int yascreen_printxyu(yascreen *s,int x,int y,uint32_t attr,const char *format,...) __attribute__((format(printf,5,6)));

prints at position; if data exceeds the buffer, then it gets truncated βœ‚οΈ

the screen is immediately updated πŸ”₯

yascreen_putsxyu

inline int yascreen_putsxyu(yascreen *s,int x,int y,uint32_t attr,const char *str);

prints at position; if data exceeds the buffer, then it gets truncated βœ‚οΈ

the screen is immediately updated πŸ”₯

yascreen_update

inline int yascreen_update(yascreen *s);

syncs memory state to the screen πŸ–₯️

since allocation is involved, this may fail and return -1

yascreen_redraw

inline void yascreen_redraw(yascreen *s);

sets the next update to be a full redraw πŸ”„

yascreen_clear_mem

inline void yascreen_clear_mem(yascreen *s,uint32_t attr);

clears the memory buffer 🧼

all cells in the screen are set to Space, using attr for colors and style 🎨

yascreen_cursor

inline void yascreen_cursor(yascreen *s,int on);

hides the cursor (on=0) or shows it (on is non-zero) πŸ‘€

the screen is updated immediately ⚑

yascreen_cursor_xy

inline void yascreen_cursor_xy(yascreen *s,int x,int y);

bestie, this one moves the cursor to a position πŸ’…

the screen gets updated immediately, no waiting around


yascreen_altbuf

inline void yascreen_altbuf(yascreen *s,int on);

flips between the regular buffer and the alternative buffer 🌈

screen updates immediately again, she does not do queues


yascreen_clear

inline void yascreen_clear(yascreen *s);

clears the real screen, and literally zero change to the memory buffers 🧹


yascreen_clearln

inline void yascreen_clearln(yascreen *s);

clears just the current line, and again, no change to memory buffers ✨


yascreen_update_attr

inline void yascreen_update_attr(yascreen *s,uint32_t oattr,uint32_t nattr);

applies the difference between two attrs and outputs the optimized ANSI sequence to switch from oattr to nattr πŸ”₯

if oattr=0xffffffff, the full ANSI sequence gets generated

no change to memory buffers, okay? none.


yascreen_set_attr

yascreen_set_attr(s,attr)

resets all attrs and sets one specific attr (attr) πŸ‘‘


yascreen_print

inline int yascreen_print(yascreen *s,const char *format,...) __attribute__((format(printf,2,3)));

yascreen_write

inline int yascreen_write(yascreen *s,const char *str,int len);

yascreen_puts

inline int yascreen_puts(yascreen *s,const char *str);

yascreen_clearln_s

inline const char *yascreen_clearln_s(yascreen *s);

prints in line mode πŸŽ€


yascreen_sx

inline int yascreen_sx(yascreen *s);

gets the current x size πŸ“


yascreen_sy

inline int yascreen_sy(yascreen *s);

gets the current y size πŸ“


yascreen_x

inline int yas<|place_holder_mm_span_0442|>in int yascreen_x(yascreen *s);

wait no, that's not it, let me not be chaotic 😭 it's exactly this:

inline int yascreen_x(yascreen *s);

gets the current x πŸ“


yascreen_y

inline int yascreen_y(yascreen *s);

gets the current y πŸ“


yascreen_esc_to

inline void yascreen_esc_to(yascreen *s,int timeout);

sets the timeout for a single ESC key press ⏳


yascreen_ckto

inline void yascreen_ckto(yascreen *s);

if you're running an external event loop, this call checks for a single ESC key πŸ‘€

it should be called regularly enough that the timeout we specified above doesn't get extended too much

if it's not called often enough, then a single ESC gets yielded after a longer timeout 😬

if it's not called at all, then a single ESC gets yielded with the next key press


yascreen_willto

inline uint64_t yascreen_willto(yascreen *s);

a single ESC key plus a timeout after it is quite the rare event, fr fr

so help your application optimize its event loop by not constantly polling yascreen_ckto 🫢

yascreen_willto tells you if there's a pending timeout, and when

returns 0 if there is no pending timeout, otherwise the remaining time in milliseconds ⏱️


yascreen_getch_to

inline int yascreen_getch_to(yascreen *s,int timeout);

waits for a key, returns ASCII or extended keycode, and waits no more than timeout in milliseconds ⌨️


yascreen_getch

yascreen_getch(s)

gets a key with no timeout, periodt

this macro expands to yascreen_getch_to(s,0)

zero timeout = wait forever 😌


yascreen_getch_nowait

yascreen_getch_nowait(s)

gets a key, if there's one available, and returns immediately ⚑

this macro expands to yascreen_getch_to(s,-1)

negative timeout = do not wait


yascreen_ungetch

inline void yascreen_ungetch(yascreen *s,int key);

puts a key value back into the key buffer ↩️

the internal key buffer is dynamically allocated, so there's literally no limit on how many key codes you can put back πŸ’– but if memory allocation fails, the error will not be reported and the key will not be put into the buffer


yascreen_pushch

inline void yascreen_pushch(yascreen *s,int key);

pushes a key value at the end of the key buffer ➑️

similar to yascreen_ungetch, but the key code gets returned after all the other key codes currently in the buffer

the internal key buffer is dynamically allocated, so again, no limit on how many key codes may be put back πŸ’– but if memory allocation fails, the error will not be reported and the key will not be put into the buffer


yascreen_feed

inline void yascreen_feed(yascreen *s,unsigned char c);

feeds the key sequence state machine with a byte stream 🌊

this is useful for implementing an external event loop and reading key codes with yascreen_getch_nowait until it returns YAS_K_NONE


yascreen_peekch

inline int yascreen_peekch(yascreen *s);

peeks for a key without removing it from the input queue πŸ‘€βœ¨


yascreen_getsize

inline void yascreen_getsize(yascreen *s,int *sx,int *sy);

gets the last reported screen size πŸ“

sets both to 0 if there isn't one

this yields a valid result after YAS_SCREEN_SIZE is returned as a keypress


yascreen_reqsize

inline void yascreen_reqsize(yascreen *s);

asks the terminal to report its size via an ANSI sequence πŸ™‹β€β™€οΈ


yascreen_set_hint_i

inline void yascreen_set_hint_i(yascreen *s,int hint);

yascreen_get_hint_i

inline int yascreen_get_hint_i(yascreen *s);

yascreen_set_hint_p

inline void yascreen_set_hint_p(yascreen *s,void *hint);

yascreen_get_hint_p

inline void *yascreen_get_hint_p(yascreen *s);

get/set opaque hint values 🎁

integer and pointer hints are stored separately and both can be used at the same time

these are useful to link the yascreen instance to user program data

for example, a single output callback may output to a socket or a terminal, depending on the hint values πŸ’…


yascreen_line_flush

inline void yascreen_line_flush(yascreen *s,int on);

enables/disables auto flush for line and direct screen oriented operations 🚰

yascreen versions before 1.77 didn't use buffered output and would immediately send the output to the screen

disabling internal flush can help an application optimize the number of write calls, at the cost of performing an explicit flush after each group of operations

explicit flush example:

yascreen_write(s,"",0);

yascreen_getwch_to

inline wchar_t yascreen_getwch_to(yascreen *s,int timeout);

waits for a key, returns a wide character or extended keycode, and waits no more than timeout in milliseconds 🌸

yascreen_getwch_to does not work in non-unicode mode and will always return YAS_K_NONE πŸ’€

mixing the utf8 and wide character input modes will break on multibyte utf8 sequences and is not supported 🚫


yascreen_getwch

yascreen_getwch(s)

gets a key as a wide character, no timeout 😌

this macro expands to yascreen_getwch_to(s,0)

zero timeout = wait forever

yascreen_getwch does not work in non-unicode mode and will always return YAS_K_NONE πŸ’€


yascreen_getwch_nowait

yascreen_getwch_nowait(s)

gets a key as a wide character, if there's one available, and returns immediately ⚑

this macro expands to yascreen_getwch_to(s,-1)

negative timeout = do not wait

yascreen_getwch_nowait does not work in non-unicode mode and will always return YAS_K_NONE πŸ’€


yascreen_ungetwch

inline void yascreen_ungetwch(yascreen *s,wchar_t key);

puts a wide character key value back into the key buffer ↩️

the internal key buffer is dynamically allocated, so no limit on how many key codes may be put back πŸ’– but if memory allocation fails, the error will not be reported and the key will not be put into the buffer

the internal key buffer contains utf8, and the wide character will be expanded to the appropriate utf8 sequence πŸ¦‹

yascreen_ungetwch does not do anything in non-unicode mode πŸ™ˆ


yascreen_peekwch

inline wchar_t yascreen_peekwch(yascreen *s);

peeks for a key without removing it from the input queue πŸ‘€

yascreen_peekwch does not work in non-unicode mode and will always return YAS_K_NONE πŸ’€


okay bestie that's the whole page, we did it πŸŽ‰ every single function, no skips, no cap. this page is from yascreen, dated July 29, 2026, and lives at YASCREEN(3) β€” go forth and slay your terminal UI πŸ’…βœ¨πŸ‘‘