ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
frame_buffer.c
Go to the documentation of this file.
1
11#include "ascii-chat/common.h"
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <stdbool.h>
17#include <unistd.h>
18#include <stdarg.h>
19
24 char *data;
25 size_t len;
26 size_t capacity;
27};
28
29frame_buffer_t *frame_buffer_create(int rows, int cols) {
31 if (!buf) {
32 return NULL;
33 }
34
35 // Initial capacity: rows * cols * 16 to account for ANSI codes
36 size_t initial_capacity = (rows > 0 && cols > 0) ? (size_t)rows * cols * 16 : 4096;
37
38 buf->data = SAFE_MALLOC(initial_capacity, char *);
39 if (!buf->data) {
40 SAFE_FREE(buf);
41 return NULL;
42 }
43
44 buf->len = 0;
45 buf->capacity = initial_capacity;
46
47 return buf;
48}
49
51 if (!buf) {
52 return;
53 }
54
55 SAFE_FREE(buf->data);
56 SAFE_FREE(buf);
57}
58
60 if (!buf) {
61 return;
62 }
63
64 buf->len = 0;
65}
66
67void frame_buffer_append(frame_buffer_t *buf, const char *str, size_t len) {
68 if (!buf || !str || len == 0) {
69 return;
70 }
71
72 // Grow buffer if needed
73 if (buf->len + len > buf->capacity) {
74 size_t new_capacity = buf->capacity * 2;
75 while (new_capacity < buf->len + len) {
76 new_capacity *= 2;
77 }
78
79 char *new_data = SAFE_MALLOC(new_capacity, char *);
80 if (!new_data) {
81 return; // Failed to grow, silently ignore
82 }
83
84 if (buf->len > 0 && buf->data) {
85 memcpy(new_data, buf->data, buf->len);
86 }
87
88 SAFE_FREE(buf->data);
89 buf->data = new_data;
90 buf->capacity = new_capacity;
91 }
92
93 // Append the data
94 memcpy(buf->data + buf->len, str, len);
95 buf->len += len;
96}
97
98void frame_buffer_printf(frame_buffer_t *buf, const char *fmt, ...) {
99 if (!buf || !fmt) {
100 return;
101 }
102
103 // Ensure we have space for a formatted string
104 // Start with a reasonable buffer size
105 size_t remaining = buf->capacity - buf->len;
106 if (remaining < 256) {
107 // Grow buffer to have at least 512 bytes of space
108 size_t new_capacity = buf->capacity + 512;
109 char *new_data = SAFE_MALLOC(new_capacity, char *);
110 if (!new_data) {
111 return;
112 }
113
114 if (buf->len > 0 && buf->data) {
115 memcpy(new_data, buf->data, buf->len);
116 }
117
118 SAFE_FREE(buf->data);
119 buf->data = new_data;
120 buf->capacity = new_capacity;
121 remaining = buf->capacity - buf->len;
122 }
123
124 // Format the string into the buffer
125 va_list args;
126 va_start(args, fmt);
127 int written = vsnprintf(buf->data + buf->len, remaining, fmt, args);
128 va_end(args);
129
130 if (written > 0 && written < (int)remaining) {
131 buf->len += (size_t)written;
132 }
133 // If vsnprintf failed or truncated, we silently ignore (buffer may be full)
134}
135
137 if (!buf) {
138 return;
139 }
140
141 frame_buffer_append(buf, "\033[H", 3);
142}
143
145 if (!buf) {
146 return;
147 }
148
149 // Combine clear screen and home in one operation
150 frame_buffer_append(buf, "\033[2J\033[H", 8);
151}
152
153// Global configuration for terminal screen output FD (used by splash/status screens)
154static int g_terminal_screen_output_fd = STDOUT_FILENO;
155
157 if (!buf || buf->len == 0 || !buf->data) {
158 return;
159 }
160
161 // Repaint from the top while erasing each line before writing it. A full
162 // screen erase on every animation frame exposes a black frame on Windows
163 // terminals and makes the splash appear to flicker.
164 size_t line_count = 1;
165 for (size_t i = 0; i < buf->len; i++) {
166 if (buf->data[i] == '\n') {
167 line_count++;
168 }
169 }
170
171 size_t output_size = 3 + buf->len + (line_count * 4);
172 char *output = SAFE_MALLOC(output_size, char *);
173 if (!output) {
174 return;
175 }
176
177 size_t output_len = 0;
178 memcpy(output + output_len, "\033[H", 3);
179 output_len += 3;
180 bool at_line_start = true;
181 for (size_t i = 0; i < buf->len; i++) {
182 if (at_line_start) {
183 memcpy(output + output_len, "\033[2K", 4);
184 output_len += 4;
185 at_line_start = false;
186 }
187 output[output_len++] = buf->data[i];
188 if (buf->data[i] == '\n') {
189 at_line_start = true;
190 }
191 }
192
193 platform_write_all(g_terminal_screen_output_fd, output, output_len);
194 SAFE_FREE(output);
195}
196
198 g_terminal_screen_output_fd = fd;
199}
200
202 return g_terminal_screen_output_fd;
203}
204
206 if (!buf) {
207 return 0;
208 }
209 return buf->len;
210}
211
213 if (!buf || !buf->data) {
214 return NULL;
215 }
216 return buf->data;
217}
218
219void frame_buffer_render_border(frame_buffer_t *buf, int cols, const char *color) {
220 if (!buf || cols <= 0) {
221 return;
222 }
223
224 if (color) {
225 frame_buffer_printf(buf, "%s━", color);
226 } else {
227 frame_buffer_printf(buf, "━");
228 }
229 for (int i = 1; i < cols - 1; i++) {
230 frame_buffer_printf(buf, "━");
231 }
232 if (color) {
233 frame_buffer_printf(buf, "\033[0m");
234 }
235 frame_buffer_printf(buf, "\n");
236}
237
238int frame_buffer_render_centered(frame_buffer_t *buf, const char *text, int cols) {
239 if (!buf || !text || cols <= 0) {
240 return 0;
241 }
242
243 // Calculate visible width accounting for ANSI codes
244 int visible_width = display_width(text);
245 if (visible_width < 0) {
246 visible_width = (int)strlen(text);
247 }
248
249 int padding = (cols - visible_width) / 2;
250 if (padding < 0) {
251 padding = 0;
252 }
253
254 for (int i = 0; i < padding; i++) {
255 frame_buffer_printf(buf, " ");
256 }
257 frame_buffer_printf(buf, "%s\n", text);
258
259 return padding;
260}
Platform abstraction layer umbrella header providing unified cross-platform API.
⚙️ Common definitions, error codes, macros, and types shared throughout the application
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.
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.
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
size_t platform_write_all(int fd, const void *buf, size_t count)
Write all bytes to a file descriptor, handling partial writes.
Definition system.c:224
int display_width(const char *text)
Calculate the visible display width of text with ANSI escape codes.
🖥️ Display Width and Terminal Centering Utilities
action_args_t args
ClangTool/LibTooling compatibility shim for stdbool.h.
Frame buffer structure.
char * data
Allocated buffer.
size_t len
Current content length.
size_t capacity
Allocated capacity.