ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
frame_buffer.c
Go to the documentation of this file.
9
12#include <libplatform/compiler.h>
13#include <libplatform/system.h>
14#include <libplatform/terminal.h>
16#include "ascii-chat/common.h"
18#include <ascii-chat/log/log.h>
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <stdbool.h>
24#include <unistd.h>
25#include <stdarg.h>
26
37 char *data;
38 size_t len;
39 size_t capacity;
40};
41
42frame_buffer_t *frame_buffer_create(int rows, int cols) {
44 if (!buf) {
45 return NULL;
46 }
47
48 // Initial capacity: rows * cols * 16 to account for ANSI codes
49 size_t initial_capacity = (rows > 0 && cols > 0) ? (size_t)rows * cols * 16 : 4096;
50
51 buf->data = SAFE_MALLOC(initial_capacity, char *);
52 if (!buf->data) {
53 SAFE_FREE(buf);
54 return NULL;
55 }
56
57 buf->len = 0;
58 buf->capacity = initial_capacity;
59
60 return buf;
61}
62
64 if (!buf) {
65 return;
66 }
67
68 SAFE_FREE(buf->data);
69 SAFE_FREE(buf);
70}
71
73 if (!buf) {
74 return;
75 }
76
77 buf->len = 0;
78}
79
80void frame_buffer_append(frame_buffer_t *buf, const char *str, size_t len) {
81 if (!buf || !str || len == 0) {
82 return;
83 }
84
85 // Grow buffer if needed
86 if (buf->len + len > buf->capacity) {
87 size_t new_capacity = buf->capacity * 2;
88 while (new_capacity < buf->len + len) {
89 new_capacity *= 2;
90 }
91
92 char *new_data = SAFE_MALLOC(new_capacity, char *);
93 if (!new_data) {
94 return; // Failed to grow, silently ignore
95 }
96
97 if (buf->len > 0 && buf->data) {
98 memcpy(new_data, buf->data, buf->len);
99 }
100
101 SAFE_FREE(buf->data);
102 buf->data = new_data;
103 buf->capacity = new_capacity;
104 }
105
106 // Append the data
107 memcpy(buf->data + buf->len, str, len);
108 buf->len += len;
109}
110
111void frame_buffer_printf(frame_buffer_t *buf, const char *fmt, ...) {
112 if (!buf || !fmt) {
113 return;
114 }
115
116 // Ensure we have space for a formatted string
117 // Start with a reasonable buffer size
118 size_t remaining = buf->capacity - buf->len;
119 if (remaining < 256) {
120 // Grow buffer to have at least 512 bytes of space
121 size_t new_capacity = buf->capacity + 512;
122 char *new_data = SAFE_MALLOC(new_capacity, char *);
123 if (!new_data) {
124 return;
125 }
126
127 if (buf->len > 0 && buf->data) {
128 memcpy(new_data, buf->data, buf->len);
129 }
130
131 SAFE_FREE(buf->data);
132 buf->data = new_data;
133 buf->capacity = new_capacity;
134 remaining = buf->capacity - buf->len;
135 }
136
137 // Format the string into the buffer
138 va_list args;
139 va_start(args, fmt);
140 int written = vsnprintf(buf->data + buf->len, remaining, fmt, args);
141 va_end(args);
142
143 if (written > 0 && written < (int)remaining) {
144 buf->len += (size_t)written;
145 }
146 // If vsnprintf failed or truncated, we silently ignore (buffer may be full)
147}
148
150 if (!buf) {
151 return;
152 }
153
154 frame_buffer_append(buf, "\033[H", 3);
155}
156
158 if (!buf) {
159 return;
160 }
161
162 // Combine clear screen and home in one operation
163 static const char clear[] = "\033[2J\033[H";
164 frame_buffer_append(buf, clear, sizeof(clear) - 1);
165}
166
167// Global configuration for terminal screen output FD (used by splash/status screens)
168static _Thread_local int g_terminal_screen_output_fd = STDOUT_FILENO;
169
171 if (!buf || buf->len == 0 || !buf->data) {
172 return;
173 }
174
175 // Repaint from the top while erasing each line before writing it. A full
176 // screen erase on every animation frame exposes a black frame on Windows
177 // terminals and makes the splash appear to flicker.
178 size_t line_count = 1;
179 for (size_t i = 0; i < buf->len; i++) {
180 if (buf->data[i] == '\n') {
181 line_count++;
182 }
183 }
184
185 size_t output_size = 3 + buf->len + (line_count * 4);
186 char *output = SAFE_MALLOC(output_size, char *);
187 if (!output) {
188 return;
189 }
190
191 size_t output_len = 0;
192 memcpy(output + output_len, "\033[H", 3);
193 output_len += 3;
194 bool at_line_start = true;
195 for (size_t i = 0; i < buf->len; i++) {
196 if (at_line_start) {
197 memcpy(output + output_len, "\033[2K", 4);
198 output_len += 4;
199 at_line_start = false;
200 }
201 output[output_len++] = buf->data[i];
202 if (buf->data[i] == '\n') {
203 at_line_start = true;
204 }
205 }
206
207 ui_output_write(g_terminal_screen_output_fd, output, output_len);
208 SAFE_FREE(output);
209}
210
212 g_terminal_screen_output_fd = fd;
214}
215
217 return g_terminal_screen_output_fd;
218}
219
221 if (!buf) {
222 return 0;
223 }
224 return buf->len;
225}
226
228 if (!buf || !buf->data) {
229 return NULL;
230 }
231 return buf->data;
232}
233
234static void render_rule(frame_buffer_t *buf, int width, const char *color, bool use_utf8) {
235 if (!buf || width <= 0)
236 return;
237 if (color)
238 frame_buffer_printf(buf, "%s", color);
239 for (int i = 0; i < width; ++i)
240 frame_buffer_printf(buf, "%s", use_utf8 ? "━" : "-");
241 if (color)
242 frame_buffer_printf(buf, "\033[0m");
243 frame_buffer_printf(buf, "\n");
244}
245
246void frame_buffer_render_border(frame_buffer_t *buf, int cols, const char *color) {
247 render_rule(buf, cols - 1, color, terminal_should_use_utf8(GET_OPTION(force_utf8)));
248}
249
250void frame_buffer_render_divider(frame_buffer_t *buf, int width, bool use_utf8, bool use_colors) {
251 render_rule(buf, width, use_colors ? ui_text_color_prefix(LOG_COLOR_DEBUG) : NULL, use_utf8);
252}
253
254int frame_buffer_render_centered(frame_buffer_t *buf, const char *text, int cols) {
255 if (!buf || !text || cols <= 0) {
256 return 0;
257 }
258
259 // Calculate visible width accounting for ANSI codes
260 int visible_width = display_width(text);
261 if (visible_width < 0) {
262 visible_width = (int)strlen(text);
263 }
264
265 int padding = (cols - visible_width) / 2;
266 if (padding < 0) {
267 padding = 0;
268 }
269
270 for (int i = 0; i < padding; i++) {
271 frame_buffer_printf(buf, " ");
272 }
273 frame_buffer_printf(buf, "%s\n", text);
274
275 return padding;
276}
action_args_t args
Definition actions.c:78
⚙️ Common definitions, error codes, macros, and types shared throughout the application
unsigned int width
Definition fft.c:23
void frame_buffer_append(frame_buffer_t *buf, const char *str, size_t len)
Append raw bytes to the buffer.
int frame_buffer_render_centered(frame_buffer_t *buf, const char *text, int cols)
Render centered text (used by both splash and status screens).
void frame_buffer_cursor_home(frame_buffer_t *buf)
Append cursor home escape code (\033[H).
void frame_buffer_set_screen_output_fd(int fd)
Set output file descriptor for terminal screen (splash/status) rendering.
void frame_buffer_reset(frame_buffer_t *buf)
Clear buffer contents while keeping allocation.
void frame_buffer_destroy(frame_buffer_t *buf)
Destroy a frame buffer.
void frame_buffer_flush(frame_buffer_t *buf)
Flush the entire buffer to stdout in one atomic write.
void frame_buffer_render_divider(frame_buffer_t *buf, int width, bool use_utf8, bool use_colors)
size_t frame_buffer_get_length(const frame_buffer_t *buf)
Get the current content length of the frame buffer.
const char * frame_buffer_get_content(const frame_buffer_t *buf)
Get a pointer to the buffer content.
frame_buffer_t * frame_buffer_create(int rows, int cols)
Create a new frame buffer.
void frame_buffer_printf(frame_buffer_t *buf, const char *fmt,...)
Printf-style append to the buffer.
int frame_buffer_get_screen_output_fd(void)
Get current output file descriptor for terminal screens.
void frame_buffer_render_border(frame_buffer_t *buf, int cols, const char *color)
Render a horizontal border line (used by both splash and status screens).
void frame_buffer_clear_screen(frame_buffer_t *buf)
Append clear screen and home escape codes (\033[2J\033[H).
Buffered terminal frame rendering.
struct frame_buffer frame_buffer_t
Opaque frame buffer handle.
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_MALLOC(size, cast)
Definition common.h:265
@ LOG_COLOR_DEBUG
Definition log/log.h:149
#define GET_OPTION(field)
Safely get a specific option field (lock-free read).
Definition options.h:1336
int colored_string_set_output_fd(int fd)
Definition string.c:274
int display_width(const char *text)
Calculate the visible display width of text with ANSI escape codes.
🖥️ Display Width and Terminal Centering Utilities
📝 Logging API with multiple log levels and terminal output control
⚙️ Unified options parsing system for ascii-chat with builder pattern and lock-free access
asciichat_error_t ui_output_write(int fd, const char *data, size_t length)
Definition output.c:16
🔤 String Manipulation and Shell Escaping Utilities
Growable byte buffer for assembling one complete terminal frame.
char * data
Allocated buffer.
size_t len
Current content length.
size_t capacity
Allocated capacity.
const char * ui_text_color_prefix(log_color_t color)
Definition text.c:18