ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
session_input.c
Go to the documentation of this file.
2#include <ascii-chat/common.h>
9static const char *COLOR_FILTER_NAMES[COLOR_FILTER_COUNT] = {
10 "None", // COLOR_FILTER_NONE = 0
11 "Black", // COLOR_FILTER_BLACK = 1
12 "White", // COLOR_FILTER_WHITE = 2
13 "Green", // COLOR_FILTER_GREEN = 3
14 "Magenta", // COLOR_FILTER_MAGENTA = 4
15 "Fuchsia", // COLOR_FILTER_FUCHSIA = 5
16 "Orange", // COLOR_FILTER_ORANGE = 6
17 "Teal", // COLOR_FILTER_TEAL = 7
18 "Cyan", // COLOR_FILTER_CYAN = 8
19 "Pink", // COLOR_FILTER_PINK = 9
20 "Red", // COLOR_FILTER_RED = 10
21 "Yellow", // COLOR_FILTER_YELLOW = 11
22 "Rainbow", // COLOR_FILTER_RAINBOW = 12
23};
24
36static double clamp_volume(double volume) {
37 if (volume < 0.0) {
38 return 0.0;
39 }
40 if (volume > 1.0) {
41 return 1.0;
42 }
43 return volume;
44}
45
49static int next_color_mode(int current) {
50 // Cycle: TERM_COLOR_NONE → TERM_COLOR_16 → TERM_COLOR_256 → TERM_COLOR_TRUECOLOR → TERM_COLOR_NONE
51 switch (current) {
52 case 0: // TERM_COLOR_NONE
53 return 1; // TERM_COLOR_16
54 case 1: // TERM_COLOR_16
55 return 2; // TERM_COLOR_256
56 case 2: // TERM_COLOR_256
57 return 3; // TERM_COLOR_TRUECOLOR
58 default:
59 return 0; // Back to TERM_COLOR_NONE
60 }
61}
62
66static int next_render_mode(int current) {
67 // 3 render modes: FOREGROUND (0), BACKGROUND (1), HALF_BLOCK (2)
68 return (current + 1) % 3;
69}
70
74static int next_color_filter(int current) {
75 return (current + 1) % COLOR_FILTER_COUNT;
76}
77
78static asciichat_error_t toggle_mute(double *saved_volume) {
79 options_snapshot_t *snapshot = SAFE_MALLOC(sizeof(*snapshot), options_snapshot_t *);
80 asciichat_error_t result;
81 for (;;) {
82 result = options_read_snapshot(snapshot);
83 if (result != ASCIICHAT_OK)
84 break;
85 double current = snapshot->values.speakers_volume;
86 double restore = *saved_volume > 0.0 ? *saved_volume : 0.5;
87 options_edit_t edit = {
88 .name = "speakers_volume", .type = OPTIONS_VALUE_DOUBLE, .value.number = current > 0.01 ? 0.0 : restore};
89 options_patch_t patch = {.edits = &edit, .count = 1, .expected_revision = snapshot->revision};
90 result = options_apply_patch(&patch, NULL);
91 if (result == ERROR_INVALID_STATE) {
93 continue;
94 }
95 if (result == ASCIICHAT_OK) {
96 if (current > 0.01)
97 *saved_volume = current;
98 log_info("%s: %.0f%%", current > 0.01 ? "Muted" : "Unmuted", edit.value.number * 100.0);
99 }
100 break;
101 }
102 SAFE_FREE(snapshot);
103 return result;
104}
105
107 switch ((int)key) {
108 case 'q':
109 return UI_ACTION_QUIT;
110 case 'Q':
112 case KEY_QUESTION:
113 return UI_ACTION_HELP;
114 case '[':
116 case ']':
117 return UI_ACTION_LOGS;
118 case KEY_ESCAPE:
119 return UI_ACTION_CLOSE;
120 case KEY_SHIFT_ESCAPE:
122 case KEY_LEFT:
123 return UI_ACTION_SEEK_BACK;
124 case KEY_RIGHT:
126 case KEY_SPACE:
127 return UI_ACTION_PAUSE;
128 case KEY_BACKTICK:
129 return UI_ACTION_DEBUG;
130 // ===== VOLUME CONTROLS =====
131 case KEY_DOWN: { // Decrease volume 10%
132 double current_volume = GET_OPTION(speakers_volume);
133 double new_volume = clamp_volume(current_volume - 0.1);
134 options_adjust("speakers_volume", key == KEY_DOWN ? -0.1 : 0.1);
135
136 double verify_volume = GET_OPTION(speakers_volume);
137 log_info("Volume DOWN: %.0f%% → %.0f%% (verified: %.0f%%)", current_volume * 100.0, new_volume * 100.0,
138 verify_volume * 100.0);
139 return UI_ACTION_VOLUME;
140 }
141
142 case KEY_UP: { // Increase volume 10%
143 double current_volume = GET_OPTION(speakers_volume);
144 double new_volume = clamp_volume(current_volume + 0.1);
145 options_adjust("speakers_volume", key == KEY_DOWN ? -0.1 : 0.1);
146
147 double verify_volume = GET_OPTION(speakers_volume);
148 log_info("Volume UP: %.0f%% → %.0f%% (verified: %.0f%%)", current_volume * 100.0, new_volume * 100.0,
149 verify_volume * 100.0);
150 return UI_ACTION_SETTINGS;
151 }
152
153 // ===== COLOR MODE CONTROL =====
154 case KEY_C:
155 case 'C': {
156 int current_mode = (int)GET_OPTION(color_mode);
157 int next_mode = next_color_mode(current_mode);
158 options_cycle("color_mode", 4);
159
160 const char *mode_names[] = {"Mono", "16-color", "256-color", "Truecolor"};
161 if (next_mode >= 0 && next_mode <= 3) {
162 log_info("Color mode: %s", mode_names[next_mode]);
163 }
164 return UI_ACTION_SETTINGS;
165 }
166
167 // ===== MUTE CONTROL =====
168 case KEY_M:
169 case 'M': {
170 if (toggle_mute(saved_volume) != ASCIICHAT_OK)
171 log_warn("Could not toggle mute");
172
173 return UI_ACTION_SETTINGS;
174 }
175
176 // ===== RENDER MODE CONTROL =====
177 case KEY_R:
178 case 'R': {
179 int current_mode = (int)GET_OPTION(render_mode);
180 int next_mode = next_render_mode(current_mode);
181 options_cycle("render_mode", 3);
182
183 const char *mode_names[] = {"Foreground", "Background", "Half-block"};
184 if (next_mode >= 0 && next_mode <= 2) {
185 log_info("Render mode: %s", mode_names[next_mode]);
186 }
187 return UI_ACTION_SETTINGS;
188 }
189
190 // ===== COLOR FILTER CONTROL =====
191 case KEY_F:
192 case 'F': {
193 int current_filter = (int)GET_OPTION(color_filter);
194 int next_filter = next_color_filter(current_filter);
195 options_cycle("color_filter", 13);
196
197 if (next_filter >= 0 && next_filter < (int)COLOR_FILTER_COUNT) {
198 log_info("Color filter: %s", COLOR_FILTER_NAMES[next_filter]);
199 }
200 return UI_ACTION_SETTINGS;
201 }
202
203 // ===== HORIZONTAL FLIP CONTROL =====
204 case 'X':
205 case 'x': {
206 log_info("✓ X KEY PRESSED - handling horizontal flip");
207 bool current_flip_x = (bool)GET_OPTION(flip_x);
208 options_toggle("flip_x");
209 log_info("Horizontal flip: %s (was: %s)", !current_flip_x ? "enabled" : "disabled",
210 current_flip_x ? "enabled" : "disabled");
211 return UI_ACTION_SETTINGS;
212 }
213
214 // ===== VERTICAL FLIP CONTROL =====
215 case 'Y':
216 case 'y': {
217 bool current_flip_y = (bool)GET_OPTION(flip_y);
218 options_toggle("flip_y");
219 log_info("Vertical flip: %s", !current_flip_y ? "enabled" : "disabled");
220 return UI_ACTION_SETTINGS;
221 }
222
223 // ===== MATRIX RAIN EFFECT CONTROL =====
224 case KEY_1: {
225 bool current_matrix = (bool)GET_OPTION(matrix_rain);
226 if (options_toggle("matrix_rain") != ASCIICHAT_OK)
227 return UI_ACTION_SETTINGS;
228 log_info("Matrix rain effect: %s", !current_matrix ? "enabled" : "disabled");
229 return UI_ACTION_SETTINGS;
230 }
231
232 case KEY_2: {
233 if (!GET_OPTION(audio_enabled)) {
234 log_info("Audio waveform requires audio to be enabled");
235 return UI_ACTION_SETTINGS;
236 }
237 bool enabled = !GET_OPTION(waveform);
238 if (options_toggle("waveform") != ASCIICHAT_OK)
239 return UI_ACTION_SETTINGS;
240 log_info("Audio waveform: %s", enabled ? "enabled" : "disabled");
241 return UI_ACTION_SETTINGS;
242 }
243
244 case KEY_3: {
245 if (!GET_OPTION(audio_enabled)) {
246 log_info("Audio frequency display requires audio to be enabled");
247 return UI_ACTION_SETTINGS;
248 }
249 bool enabled = !GET_OPTION(fft);
251 log_error("Unable to initialize FFTW frequency display");
252 return UI_ACTION_SETTINGS;
253 }
254 if (options_toggle("fft") != ASCIICHAT_OK)
255 return UI_ACTION_SETTINGS;
256 log_info("Audio frequency display: %s", enabled ? "enabled" : "disabled");
257 return UI_ACTION_SETTINGS;
258 }
259
260 case 'w':
261 case 'W': {
262 if (options_toggle("face_follow") == ASCIICHAT_OK)
263 log_info("Face follow: %s", GET_OPTION(face_follow) ? "enabled" : "disabled");
264 return UI_ACTION_SETTINGS;
265 }
266
267 case 'z':
268 case 'Z': {
269 if (options_set_int("zalgo", GET_OPTION(zalgo) ? 0 : 8) == ASCIICHAT_OK)
270 log_info("Zalgo intensity: %d", GET_OPTION(zalgo));
271 return UI_ACTION_SETTINGS;
272 }
273
274 // ===== FPS COUNTER TOGGLE =====
275 case KEY_MINUS: {
276 bool current = (bool)GET_OPTION(fps_counter);
277 options_toggle("fps_counter");
278 log_info("FPS counter: %s", !current ? "enabled" : "disabled");
279 return UI_ACTION_SETTINGS;
280 }
281
282 default:
283 return UI_ACTION_NONE;
284 }
285}
#define CLEAR_ERRNO()
⚙️ Common definitions, error codes, macros, and types shared throughout the application
int color_mode
Definition fft.c:25
asciichat_error_t audio_visualization_fft_init(void)
Definition fft.c:61
bool enabled
Is filtering active?
Definition grep.c:62
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_MALLOC(size, cast)
Definition common.h:265
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ERROR_INVALID_STATE
@ ASCIICHAT_OK
Definition error_codes.h:46
#define log_warn(...)
Log a WARN message.
Definition log/log.h:621
#define log_error(...)
Log an ERROR message.
Definition log/log.h:634
#define log_info(...)
Log an INFO message.
Definition log/log.h:608
#define GET_OPTION(field)
Safely get a specific option field (lock-free read).
Definition options.h:1336
asciichat_error_t options_set_int(const char *name, int value)
Check if an option was explicitly set via command-line.
Definition rcu.c:894
keyboard_key_t
Options for interactive line editing.
Definition keyboard.h:53
@ KEY_SPACE
Space bar.
Definition keyboard.h:56
@ KEY_UP
Up arrow key.
Definition keyboard.h:57
@ KEY_M
'm' key - toggle mute
Definition keyboard.h:81
@ KEY_SHIFT_ESCAPE
Shift+Escape (immediate exit).
Definition keyboard.h:73
@ KEY_2
'2' key - toggle audio waveform
Definition keyboard.h:77
@ KEY_BACKTICK
'`' key - print lock state (debug builds)
Definition keyboard.h:84
@ KEY_1
'1' key - toggle matrix rain effect
Definition keyboard.h:76
@ KEY_ESCAPE
Escape key (ESC).
Definition keyboard.h:55
@ KEY_F
'f' key - flip webcam
Definition keyboard.h:82
@ KEY_MINUS
'-' key - toggle FPS counter
Definition keyboard.h:74
@ KEY_LEFT
Left arrow key.
Definition keyboard.h:59
@ KEY_RIGHT
Right arrow key.
Definition keyboard.h:60
@ KEY_C
'c' key - cycle color modes
Definition keyboard.h:79
@ KEY_R
'r' key - cycle render modes
Definition keyboard.h:80
@ KEY_DOWN
Down arrow key.
Definition keyboard.h:58
@ KEY_QUESTION
'?' key - show help screen
Definition keyboard.h:83
@ KEY_3
'3' key - toggle audio frequency display
Definition keyboard.h:78
📝 Logging API with multiple log levels and terminal output control
@ OPTIONS_VALUE_DOUBLE
⚙️ Unified options parsing system for ascii-chat with builder pattern and lock-free access
asciichat_error_t options_adjust(const char *name, double amount)
Definition rcu.c:914
asciichat_error_t options_apply_patch(const options_patch_t *patch, options_patch_result_t *result)
Definition rcu.c:837
asciichat_error_t options_toggle(const char *name)
Definition rcu.c:911
asciichat_error_t options_read_snapshot(options_snapshot_t *snapshot)
Definition rcu.c:865
asciichat_error_t options_cycle(const char *name, int count)
Definition rcu.c:918
ui_session_action_t ui_session_key(keyboard_key_t key, double *saved_volume)
union options_edit_t::@262352151300275353235176125230070205337075074271 value
float speakers_volume
Speaker volume multiplier (0.0-1.0, default 1.0).
Definition options.h:1131