CX Framework
Cross-platform C utility framework
Loading...
Searching...
No Matches
console_private.h
1#pragma once
2
3// Every console-related C source file MUST begin with:
4//
5// #ifdef CX_LOCK_DEBUG
6// #undef CX_LOCK_DEBUG
7// #endif
8//
9// as its literal first lines, before any #include -- exactly like every cx/log/*.c file
10// does. Under CX_LOCK_DEBUG, mutex.h redefines mutexAcquire()/mutexRelease() to log, and a
11// console stream taking its own lock while that is active would log, which reaches
12// logconsole, which deadlocks.
13
14#include <cx/thread/atomic.h>
15#include <cx/thread/mutex.h>
16#include "concursor.h"
17#include "conin.h"
18#include "constyle.h"
19
20CX_C_BEGIN
21
22typedef enum ConKind {
23 CON_Kind_Out,
24 CON_Kind_Err,
25 CON_Kind_In,
26 CON_Kind_Mem,
27} ConKind;
28
29#define CONBUF_DEFAULT_SIZE 4096
30
31struct ConStream {
32 ConKind kind;
33
34 Mutex lock;
35 atomic(intptr) owner; // thrCurrentOSThreadID() of the current holder; 0 == free
36 uint32 depth; // owner-only; valid only while the lock is held
37
38 ConCaps caps;
39
40 uint8* buf; // NULL for CON_Kind_Mem
41 uint32 bufsz;
42 uint32 bufused;
43 bool linebuffered; // flush when a write contains '\n'
44 bool autoflush; // flush after every write (conErr())
45
46 ConStyle curStyle; // exactly what the caller last passed to conSetStyle/conResetStyle
47 bool styleActive; // curStyle differs from the all-default style; drives conShutdown()
48
49 string memcapture; // CON_Kind_Mem only
50
51 void* plat; // platform-owned; opaque to portable code
52};
53
54// Internal write helper (conout.c) shared with constyle.c. con must already be locked by the
55// caller.
56bool _conWriteLocked(_Inout_ ConStream* con, _In_reads_bytes_(sz) const uint8* buf, size_t sz);
57
58// Internal UTF-8 encoder (conout.c) shared with constyle.c. Returns the number of bytes
59// written to out (1-4). An invalid code point is replaced with U+FFFD.
60uint32 _conUtf8Encode(_Out_writes_(4) uint8 out[4], int32 cp);
61
62// Internal UTF-8 decoder (conout.c) shared with conin.c's unix backend. Decodes the single
63// codepoint starting at buf[0], writing it to *cp (U+FFFD on an invalid or truncated
64// sequence) and returning the number of bytes consumed -- always >= 1 and <= len, so the
65// caller can always advance. len is how many bytes are actually available, which may be
66// fewer than the sequence needs if a read was interrupted; this never reads past it.
67uint32 _conUtf8Decode(_In_reads_bytes_(len) const uint8* buf, uint32 len, _Out_ int32* cp);
68
69// Internal decimal-ASCII appenders (conout.c) shared with constyle.c and concursor.c -- both
70// build VT escape sequences out of small unsigned numbers. _conAppendDec writes the bare
71// digits; _conAppendCode prefixes them with ';', the separator every SGR/CSI parameter after
72// the first one needs. Neither NUL-terminates; the caller tracks its own length.
73uint32 _conAppendDec(char* buf, uint32 pos, uint32 v);
74uint32 _conAppendCode(char* buf, uint32 pos, uint32 code);
75
76// --- platform primitives, implemented once per platform selected at link time ---
77
78// Allocates and initializes the platform-specific portion of a real (non-memory) stream:
79// determines the underlying OS handle, detects con->caps (via _conDetectCapsAuto plus
80// whatever platform probing it can add), and stores whatever the platform needs in
81// con->plat. Called exactly once per singleton, from the lazy-init callbacks in console.c.
82// Never called for CON_Kind_Mem.
83void _conPlatInit(_Inout_ ConStream* con, ConKind kind);
84
85// Writes sz raw bytes to the underlying OS handle, retrying on partial writes. Returns
86// true on success. Never called for CON_Kind_Mem.
87bool _conPlatWrite(_Inout_ ConStream* con, _In_reads_bytes_(sz) const void* buf, size_t sz);
88
89// Re-queries the current terminal size and updates con->caps.width/height. A no-op if the
90// stream is not a tty.
91void _conPlatQuerySize(_Inout_ ConStream* con);
92
93// Restores anything _conPlatInit changed and releases con->plat. Called once per real
94// stream from conShutdown().
95void _conPlatShutdown(_Inout_ ConStream* con);
96
97// Sets text/background color and attributes on a stream whose VT/ANSI escape sequences are
98// not usable (con->caps.vt == false) but which still has color capability -- today, only the
99// Windows legacy console falling back from a failed ENABLE_VIRTUAL_TERMINAL_PROCESSING probe.
100// style.fg/bg have already been downgraded by constyle.c to CON_ColorDefault or CON_Idx(0-15)
101// before this is called, so implementations never need to handle CON_Color256/RGB. On
102// unix/wasm con->caps.vt is always true whenever con->caps.color != CON_ColorNone (see
103// concaps.c's ladder), so this is never actually reached there; their implementations exist
104// only to satisfy the link and devAssert if that assumption is ever violated. Never called
105// for CON_Kind_Mem.
106bool _conPlatSetStyleLegacy(_Inout_ ConStream* con, ConStyle style);
107
108// --- legacy cursor/screen backend, used only when caps.cursor is true but caps.vt is false ---
109//
110// Today that combination exists only on Windows falling back from a failed
111// ENABLE_VIRTUAL_TERMINAL_PROCESSING probe (see win_console.c's _conPlatInit). On unix/wasm
112// caps.cursor is always false whenever caps.vt is false (concaps.c sets caps.cursor = vt
113// outright), so concursor.c never reaches these there; their implementations exist only to
114// satisfy the link and devAssert if that assumption is ever violated. Never called for
115// CON_Kind_Mem -- a memory stream reporting caps.cursor without caps.vt is a malformed test
116// fixture, not a real backend.
117
118// row/col are 0-based, already clamped to the stream's actual buffer/window bounds by the
119// caller where that matters (conMoveCursor()).
120bool _conPlatCursorSet(_Inout_ ConStream* con, uint16 row, uint16 col);
121bool _conPlatCursorGet(_Inout_ ConStream* con, _Out_ uint16* row, _Out_ uint16* col);
122bool _conPlatCursorShow(_Inout_ ConStream* con, bool show);
123
124// Save/restore the cursor position across the legacy backend, where (unlike VT's DECSC/DECRC)
125// there is no terminal-side save slot -- the platform file remembers the position itself.
126bool _conPlatCursorSave(_Inout_ ConStream* con);
127bool _conPlatCursorRestore(_Inout_ ConStream* con);
128
129bool _conPlatEraseLine(_Inout_ ConStream* con, ConEraseMode mode);
130bool _conPlatEraseScreen(_Inout_ ConStream* con, ConEraseMode mode);
131
132// Positive lines scrolls content up (new blank lines appear at the bottom); negative scrolls
133// down. Matches conScroll()'s sign convention.
134bool _conPlatScroll(_Inout_ ConStream* con, int16 lines);
135
136// --- capability detection, implemented in concaps.c (portable, no platform dependency) ---
137
138// Pure, unit-testable capability detection from explicit inputs -- no I/O of its own. NULL
139// means the corresponding environment variable was unset. Windows-only inputs (wt_session,
140// conemuansi, term_program) are harmless to pass as NULL on other platforms.
141//
142// `termless` is what an unset TERM should be taken to mean on this platform, and it is the
143// one input that is not an environment variable. On unix an unset TERM is a real signal that
144// there is no terminal to speak ANSI at, so the platform file passes CON_ColorNone. Windows
145// consoles never set TERM at all, so it set an appropriate default based on API capabilities.
146//
147// NO_COLOR is applied after `termless`, so it still wins on every platform.
148void _conDetectCaps(_Out_ ConCaps* out, bool istty, ConColorDepth termless,
149 _In_opt_z_ const char* term, _In_opt_z_ const char* colorterm,
150 _In_opt_z_ const char* no_color, _In_opt_z_ const char* force_color,
151 _In_opt_z_ const char* clicolor_force, _In_opt_z_ const char* wt_session,
152 _In_opt_z_ const char* conemuansi, _In_opt_z_ const char* term_program,
153 _In_opt_z_ const char* lang);
154
155// Convenience wrapper that reads the environment itself via getenv() and forwards to
156// _conDetectCaps(). Platform files call this after determining `istty` and `termless`.
157void _conDetectCapsAuto(_Out_ ConCaps* out, bool istty, ConColorDepth termless);
158
159// --- escape-sequence decoding, implemented in conin.c (portable, no platform dependency) ---
160//
161// Used only by the unix backend -- Windows gets structured key events straight from
162// ReadConsoleInputW and never has raw escape bytes to decode; wasm has no input at all.
163
164typedef enum ConDecodeResult {
165 CON_Decode_Incomplete, // a valid prefix so far, but more bytes are needed to know which
166 // sequence this is; the caller should read more and retry
167 CON_Decode_Matched, // *out and *consumed are filled in
168 CON_Decode_NoMatch, // buf[0..1] is not a sequence this module recognizes
169} ConDecodeResult;
170
171// buf[0] must be ESC (0x1B); len is how many bytes are available to look at so far. Pure and
172// unit-testable directly, like _conDetectCaps -- no I/O, no platform dependency.
173ConDecodeResult _conDecodeEscape(_In_reads_bytes_(len) const uint8* buf, uint32 len,
174 _Out_ ConKeyEvent* out, _Out_ uint32* consumed);
175
176// --- input platform primitives, implemented once per platform selected at link time ---
177//
178// All four are meaningful only for CON_Kind_In and return false immediately otherwise
179// (including CON_Kind_Mem -- there is no real input behind a memory stream). None of these
180// take con->lock themselves; conin.c's public wrappers do that, and the crash-handler
181// restore path calls _conPlatSetMode()/_conPlatSetEcho() directly, bypassing the lock
182// entirely, specifically so it can run from a crash callback without risking the deadlock a
183// mutex acquisition could cause there (see conin.c).
184
185bool _conPlatSetMode(_Inout_ ConStream* con, ConInputMode mode);
186bool _conPlatSetEcho(_Inout_ ConStream* con, bool echo);
187bool _conPlatInWait(_Inout_ ConStream* con, int64 timeout);
188bool _conPlatReadKey(_Inout_ ConStream* con, _Out_ ConKeyEvent* out, int64 timeout);
189
190// Reads one raw byte from CON_Kind_In's underlying handle with no tty/key decoding of any
191// kind -- used only by conin.c's redirected-input fallback (conGetCaps().istty == false), so
192// that conReadLine()/conReadPassword() work when stdin is a pipe or file instead of failing
193// outright. Unlike the four hooks above, this is meaningful (and expected to be reached)
194// whether or not the stream is a real terminal; on wasm, where istty is always false, it is
195// the ordinary path rather than an unreachable one. Blocks until a byte arrives or the
196// underlying handle hits EOF/error, in which case it returns false.
197bool _conPlatReadRawByte(_Inout_ ConStream* con, _Out_ uint8* out);
198
199CX_C_END
Atomic operations.
Cursor positioning and screen erase/scroll operations.
Keyboard input: raw mode, key decoding, line/password reading.
Console text styling: colors, attributes, and styled writes.
struct ConStream ConStream
Definition console.h:20
ConColorDepth
Color depth a stream is able to render.
Definition console.h:23
ConEraseMode
Which portion of a line or screen to erase, relative to the current cursor position.
Definition concursor.h:15
ConInputMode
Input mode for a stream. See conSetMode().
Definition conin.h:71
Mutex synchronization primitive.
A single decoded key press.
Definition conin.h:62
Definition mutex.h:60