maug
Quick and dirty C mini-augmentation library.
Loading...
Searching...
No Matches
retrocon.h
1
2#ifndef RETROCON_H
3#define RETROCON_H
4
5#define RETROCON_DEBOUNCE_WAIT 3
6
7#define RETROCON_FLAG_ACTIVE 0x01
8
9#define RETROCON_IDC_TEXTBOX 1
10
11#define RETROCON_IDC_CON_BASE 10
12
13#define RETROCON_IDC_CLOSE (-1)
14
15#ifndef RETROCON_TRACE_LVL
16# define RETROCON_TRACE_LVL 0
17#endif /* !RETROCON_TRACE_LVL */
18
19#ifdef RETROCON_DISABLE
20
21struct RETROCON {
22 uint8_t flags;
23 RETROFLAT_COLOR lbuffer_color;
24 RETROFLAT_COLOR sbuffer_color;
25 RETROFLAT_COLOR bg_color;
26};
27
28# define retrocon_init( con, fn, x, y, w, h ) (MERROR_OK)
29
30# define retrocon_add_command( con, cmd, cb, cb_data ) (MERROR_OK)
31
32# define retrocon_display( con, display )
33
34# define retrocon_print_line( con, line )
35
36# define retrocon_exec_line( con, line, line_sz )
37
38# define retrocon_debounce( con, c )
39
40# define retrocon_input( con, p_c, input_evt, p_idc, win_stack )
41
42# define retrocon_shutdown( con )
43
44#else
45
50
51#ifndef RETROCON_TRACE_LVL
52# define RETROCON_TRACE_LVL 0
53#endif /* !RETROCON_TRACE_LVL */
54
55#ifndef RETROCON_SBUFFER_SZ_MAX
56# define RETROCON_SBUFFER_SZ_MAX 4096
57#endif /* !RETROCON_SBUFFER_SZ_MAX */
58
59#ifndef RETROCON_SBUFFER_LINES_MAX
60# define RETROCON_SBUFFER_LINES_MAX 30
61#endif /* !RETROCON_SBUFFER_LINES_MAX */
62
63#ifndef RETROCON_LBUFFER_SZ_MAX
64# define RETROCON_LBUFFER_SZ_MAX 256
65#endif /* !RETROCON_LBUFFER_SZ_MAX */
66
67#ifndef RETROCON_ACTIVE_KEY
68# define RETROCON_ACTIVE_KEY RETROFLAT_KEY_GRAVE
69#endif /* !RETROCON_ACTIVE_KEY */
70
71#ifndef RETROCON_CB_NAME_SZ_MAX
72# define RETROCON_CB_NAME_SZ_MAX 32
73#endif /* !RETROCON_CB_NAME_SZ_MAX */
74
75#ifndef RETROCON_CB_SZ_MAX
76# define RETROCON_CB_SZ_MAX 128
77#endif /* !RETROCON_CB_SZ_MAX */
78
79struct RETROCON;
80
81typedef MERROR_RETVAL (*retrocon_cb)(
82 struct RETROCON* con, const char* line, size_t line_sz, void* data );
83
84struct RETROCON {
85 uint8_t flags;
86 struct RETROGUI gui;
87 int input_prev;
88 int debounce_wait;
89 void* callback_data[RETROCON_CB_SZ_MAX];
90 char callback_names[RETROCON_CB_SZ_MAX][RETROCON_CB_NAME_SZ_MAX + 1];
91 retrocon_cb callbacks[RETROCON_CB_SZ_MAX];
92 size_t callbacks_sz;
93 RETROFLAT_COLOR lbuffer_color;
94 RETROFLAT_COLOR sbuffer_color;
95 RETROFLAT_COLOR bg_color;
96 size_t sbuffer_lines;
97};
98
99MERROR_RETVAL retrocon_init(
100 struct RETROCON* con, const char* font_name,
101 size_t x, size_t y, size_t w, size_t h );
102
103MERROR_RETVAL retrocon_add_command(
104 struct RETROCON* con, const char* cmd, retrocon_cb cb, void* cb_data );
105
106MERROR_RETVAL retrocon_print_line( struct RETROCON* con, const char* line );
107
108MERROR_RETVAL retrocon_exec_line(
109 struct RETROCON* con, char* line, size_t line_sz );
110
111int retrocon_debounce( struct RETROCON* con, int c );
112
127 struct RETROCON* con, RETROFLAT_IN_KEY* p_c,
128 struct RETROFLAT_INPUT* input_evt,
129 retrogui_idc_t* p_idc_out, struct MDATA_VECTOR* win_stack );
130
131MERROR_RETVAL retrocon_display(
132 struct RETROCON* con, retroflat_blit_t* gui_bmp );
133
134void retrocon_shutdown( struct RETROCON* con );
135
136#ifdef RETROCON_C
137
138static MERROR_RETVAL retrocon_cmd_print(
139 struct RETROCON* con, const char* line, size_t line_sz, void* data
140) {
141 MERROR_RETVAL retval = MERROR_OK;
142 char* print_line = NULL;
143
144 print_line = maug_strchr( line, ' ' );
145 if( NULL == print_line ) {
146 /* Not technically an error. */
147 goto cleanup;
148 }
149
150 /* Skip space. */
151 print_line++;
152
153 retrocon_print_line( con, print_line );
154
155cleanup:
156
157 return retval;
158}
159
160static MERROR_RETVAL retrocon_cmd_quit(
161 struct RETROCON* con, const char* line, size_t line_sz, void* data
162) {
163 MERROR_RETVAL retval = MERROR_OK;
164
165 retroflat_quit( 0 );
166
167 return retval;
168}
169
170MERROR_RETVAL retrocon_init(
171 struct RETROCON* con, const char* font_name,
172 size_t x, size_t y, size_t w, size_t h
173) {
174 MERROR_RETVAL retval = MERROR_OK;
175 union RETROGUI_CTL ctl;
176 size_t ctl_y_iter = 0;
177
178 debug_printf( RETROCON_TRACE_LVL, "initializing console..." );
179
180 retval = retrogui_init( &(con->gui) );
181 maug_cleanup_if_not_ok();
182
183 /* TODO: Parse font height from filename and only load printable glyphs. */
184#ifdef RETROGXC_PRESENT
185 con->gui.font_idx = retrogxc_load_font( font_name, 0, 33, 93 );
186#else
187 retval = retrofont_load( font_name, &(con->gui.font_h), 0, 33, 93 );
188#endif /* RETROGXC_PRESENT */
189 maug_cleanup_if_not_ok();
190
191 con->sbuffer_color = RETROFLAT_COLOR_DARKBLUE;
192 con->lbuffer_color = RETROFLAT_COLOR_BLACK;
193 con->gui.x = x;
194 con->gui.y = y;
195 con->gui.w = w;
196 con->gui.h = h;
197
198 retrogui_init_ctl(
199 &ctl, RETROGUI_CTL_TYPE_TEXTBOX, RETROCON_IDC_TEXTBOX );
200
201 ctl.base.x = 5;
202 ctl.base.y = 5;
203 ctl.base.w = con->gui.w - 10;
204 ctl.base.h = 20; /* TODO: Dynamic height based on font. */
205 ctl.base.fg_color = RETROFLAT_COLOR_BLACK;
206
207 retval = retrogui_push_ctl( &(con->gui), &ctl );
208 maug_cleanup_if_not_ok();
209
210 ctl_y_iter = ctl.base.y + ctl.base.h + 1;
211 while( h - 5 > ctl_y_iter + 8 + 1 ) {
212 retrogui_init_ctl(
213 &ctl, RETROGUI_CTL_TYPE_LABEL,
214 RETROCON_IDC_CON_BASE + con->sbuffer_lines );
215
216 ctl.base.x = 5;
217 ctl.base.y = ctl_y_iter;
218 ctl.base.w = con->gui.w - 10;
219 ctl.base.h = 8; /* TODO: Dynamic height based on font. */
220 ctl.base.fg_color = RETROFLAT_COLOR_DARKGRAY;
221 ctl.LABEL.label = "xxxxxx";
222
223 retval = retrogui_push_ctl( &(con->gui), &ctl );
224 maug_cleanup_if_not_ok();
225
226 ctl_y_iter += ctl.base.h + 1;
227 con->sbuffer_lines++;
228 }
229
230 con->gui.focus = RETROCON_IDC_TEXTBOX;
231
232 retval = retrocon_add_command( con, "PRINT", retrocon_cmd_print, NULL );
233 maug_cleanup_if_not_ok();
234 retval = retrocon_add_command( con, "QUIT", retrocon_cmd_quit, NULL );
235 maug_cleanup_if_not_ok();
236
237cleanup:
238
239 return retval;
240}
241
242MERROR_RETVAL retrocon_add_command(
243 struct RETROCON* con, const char* cmd, retrocon_cb cb, void* cb_data
244) {
245 MERROR_RETVAL retval = MERROR_OK;
246
247 debug_printf( RETROCON_TRACE_LVL, "adding console command: %s", cmd );
248
249 maug_cleanup_if_ge_overflow( con->callbacks_sz + 1, RETROCON_CB_SZ_MAX );
250
251 maug_strncpy(
252 con->callback_names[con->callbacks_sz], cmd, RETROCON_CB_NAME_SZ_MAX );
253
254 con->callbacks[con->callbacks_sz] = cb;
255
256 con->callback_data[con->callbacks_sz] = cb_data;
257
258 con->callbacks_sz++;
259
260cleanup:
261
262 return retval;
263}
264
265/* === */
266
267MERROR_RETVAL retrocon_print_line( struct RETROCON* con, const char* line ) {
268 char sbuffer_shift[RETROCON_LBUFFER_SZ_MAX + 1] = { 0 };
269 size_t i = 0;
270 MERROR_RETVAL retval = MERROR_OK;
271
272 /* TODO: Escape newlines in line. */
273
274 /* Shift existing screen buffer lines all down by one. */
275 for( i = con->sbuffer_lines - 1 ; 0 < i ; i-- ) {
276 debug_printf( RETROCON_TRACE_LVL,
277 "copying sbuffer line " SIZE_T_FMT " to " SIZE_T_FMT "...",
278 i - 1, i );
279 maug_mzero( sbuffer_shift, RETROCON_LBUFFER_SZ_MAX + 1 );
280 retval = retrogui_get_ctl_text(
281 &(con->gui), RETROCON_IDC_CON_BASE + i - 1,
282 sbuffer_shift, RETROCON_LBUFFER_SZ_MAX );
283 retval = retrogui_set_ctl_text(
284 &(con->gui), RETROCON_IDC_CON_BASE + i,
285 RETROCON_LBUFFER_SZ_MAX, sbuffer_shift );
286 maug_cleanup_if_not_ok();
287 }
288
289 /* Put line in first sbuffer line. */
290 retval = retrogui_set_ctl_text(
291 &(con->gui), RETROCON_IDC_CON_BASE,
292 RETROCON_LBUFFER_SZ_MAX, line );
293 maug_cleanup_if_not_ok();
294
295cleanup:
296
297 return retval;
298}
299
300/* === */
301
302MERROR_RETVAL retrocon_exec_line(
303 struct RETROCON* con, char* line, size_t line_sz
304) {
305 MERROR_RETVAL retval = MERROR_OK;
306 size_t i = 0;
307 char line_cap[RETROCON_LBUFFER_SZ_MAX + 1];
308
309 /* Create an uppercase for comparison. */
310 maug_mzero( line_cap, RETROCON_LBUFFER_SZ_MAX + 1 );
311 maug_strncpy( line_cap, line, RETROCON_LBUFFER_SZ_MAX );
312 maug_str_upper( line_cap, line_sz );
313
314 /* Find callback with name starting line. */
315 for( i = 0 ; con->callbacks_sz > i ; i++ ) {
316 if(
317 0 == strncmp(
318 /* TODO: Compare up to first space in line. */
319 con->callback_names[i], line_cap,
320 maug_strlen( con->callback_names[i] ) )
321 ) {
322 retval = con->callbacks[i](
323 con, line, line_sz, con->callback_data[i] );
324 goto cleanup;
325 }
326 }
327
328 retrocon_print_line( con, "COMMAND NOT FOUND!" );
329
330cleanup:
331
332 return retval;
333}
334
335/* === */
336
338 struct RETROCON* con, RETROFLAT_IN_KEY* p_c,
339 struct RETROFLAT_INPUT* input_evt,
340 retrogui_idc_t* p_idc_out, struct MDATA_VECTOR* win_stack
341) {
342 MERROR_RETVAL retval = MERROR_OK;
343 char lbuffer[RETROCON_LBUFFER_SZ_MAX + 1] = { 0 };
344
345 *p_idc_out = RETROGUI_IDC_NONE;
346
347 if( *p_c == RETROCON_ACTIVE_KEY ) {
348 debug_printf( RETROCON_TRACE_LVL, "active key pressed" );
349 if( RETROCON_FLAG_ACTIVE != (RETROCON_FLAG_ACTIVE & (con)->flags) ) {
350 debug_printf( RETROCON_TRACE_LVL, "opening console..." );
351 (con)->flags |= RETROCON_FLAG_ACTIVE;
352 (con)->gui.flags |= RETROGUI_FLAGS_DIRTY;
353
354 if( NULL != win_stack ) {
355 retval = retrowin_push_win(
356 &((con)->gui), win_stack,
357 /* Use the IDC provided in p_idc_out for the window IDC. */
358 *p_idc_out,
359 NULL, (con)->gui.x, (con)->gui.y,
360 (con)->gui.w, (con)->gui.h, RETROWIN_FLAG_BORDER_BLUE );
361 maug_cleanup_if_not_ok();
362 }
363
364 debug_printf( RETROCON_TRACE_LVL, "console open!" );
365 } else {
366 debug_printf( RETROCON_TRACE_LVL, "closing console..." );
367 con->flags &= ~RETROCON_FLAG_ACTIVE;
368
369 if( NULL != win_stack ) {
370 retrowin_destroy_win( win_stack, *p_idc_out );
371 debug_printf( RETROCON_TRACE_LVL, "console closed!" );
372 }
373
374# if defined( RETROTILE_PRESENT ) && !defined( RETROFLAT_NO_VIEWPORT_REFRESH )
375 /* Mark all tiles as dirty when console is opened/closed. */
376 retroflat_viewport_lock_refresh();
377 retval = retrotile_clear_refresh( retroflat_viewport_screen_h() );
378 retroflat_viewport_unlock_refresh();
379# endif /* RETROTILE_PRESENT && !RETROFLAT_NO_VIEWPORT_REFRESH */
380
381 /* Return, in case the caller needs to do something with this. */
382 *p_idc_out = RETROCON_IDC_CLOSE;
383 }
384 *p_c = 0;
385 goto cleanup;
386 } else if( RETROCON_FLAG_ACTIVE != (RETROCON_FLAG_ACTIVE & (con)->flags) ) {
387 goto cleanup;
388 }
389
390 /* debug_printf( RETROCON_TRACE_LVL, "processing console input..." ); */
391
392 *p_idc_out = retrogui_poll_ctls( &(con->gui), p_c, input_evt );
393
394 *p_c = 0; /* If we got this far then don't pass keystroke back. */
395
396 if( RETROCON_IDC_TEXTBOX == *p_idc_out ) {
397 retval = retrogui_get_ctl_text(
398 &(con->gui), RETROCON_IDC_TEXTBOX, lbuffer, RETROCON_LBUFFER_SZ_MAX );
399 retval = retrogui_set_ctl_text(
400 &(con->gui), RETROCON_IDC_TEXTBOX, 1, "" );
401 maug_cleanup_if_not_ok();
402
403 if( 0 == maug_strlen( lbuffer ) ) {
404 /* Do nothing if line is empty. */
405 goto cleanup;
406 }
407
408 /* Execute/reset line. */
409 retval = retrocon_exec_line( con, lbuffer, maug_strlen( lbuffer ) );
410 }
411
412#if 0
413 /* Debounce retrocon track only! */
414 if( !retrocon_debounce( con, c ) ) {
415 goto cleanup;
416 }
417
418 /* Process input. */
419 switch( c ) {
420 case RETROCON_ACTIVE_KEY:
421 if( RETROCON_FLAG_ACTIVE == (RETROCON_FLAG_ACTIVE & con->flags) ) {
422 con->flags &= ~RETROCON_FLAG_ACTIVE;
423 } else {
424 con->flags |= RETROCON_FLAG_ACTIVE;
425 }
426 break;
427
428 case 0:
429 break;
430
431 case 0x08:
432 /* Backspace. */
433 if( 0 < con->lbuffer_sz ) {
434 con->lbuffer_sz--;
435 con->lbuffer[con->lbuffer_sz] = '\0';
436 }
437 break;
438
439 case '\r':
440 case '\n':
441 if( 0 == con->lbuffer_sz ) {
442 /* Do nothing if line is empty. */
443 break;
444 }
445
446 /* Execute/reset line. */
447 retval = retrocon_exec_line( con, con->lbuffer, con->lbuffer_sz );
448 con->lbuffer_sz = 0;
449 con->lbuffer[con->lbuffer_sz] = '\0';
450 break;
451
452 default:
453 c = retroflat_vk_to_ascii(
454 c, input_evt->key_flags | RETROFLAT_INPUT_FORCE_UPPER );
455 if(
456 /* Active and printable chars get added to line buffer. */
457 RETROCON_FLAG_ACTIVE == (RETROCON_FLAG_ACTIVE & con->flags) &&
458 0 < c
459 ) {
460 con->lbuffer[con->lbuffer_sz++] = c;
461 con->lbuffer[con->lbuffer_sz] = '\0';
462 }
463 break;
464 }
465#endif
466
467cleanup:
468
469 return retval;
470}
471
472/* === */
473
474MERROR_RETVAL retrocon_display(
475 struct RETROCON* con, retroflat_blit_t* gui_bmp
476) {
477 MERROR_RETVAL retval = MERROR_OK;
478
479 if( RETROCON_FLAG_ACTIVE != (RETROCON_FLAG_ACTIVE & (con)->flags) ) {
480 goto cleanup;
481 }
482
483 (con)->gui.draw_bmp = (gui_bmp);
484 (con)->gui.flags |= RETROGUI_FLAGS_DIRTY;
485
486 retrogui_redraw_ctls( &((con)->gui) );
487
488cleanup:
489
490 return retval;
491}
492
493/* === */
494
495void retrocon_shutdown( struct RETROCON* con ) {
496#ifndef RETROGXC_PRESENT
497 retrofont_free( &(con->gui.font_h) );
498#endif /* !RETROGXC_PRESENT */
499 retrogui_destroy( &(con->gui) );
500}
501
502#endif /* RETROCON_C */
503 /* maug_console */
505
506#endif /* RETROCON_DISABLE */
507
508#endif /* !RETROCON_H */
509
MERROR_RETVAL retrocon_input(struct RETROCON *con, RETROFLAT_IN_KEY *p_c, struct RETROFLAT_INPUT *input_evt, retrogui_idc_t *p_idc_out, struct MDATA_VECTOR *win_stack)
Process input from retroflat_poll_input() and apply it to the console, if open.
int MERROR_RETVAL
Return type indicating function returns a value from this list.
Definition merror.h:19
int8_t RETROFLAT_COLOR
Defines an index in the platform-specific color-table.
Definition retroflt.h:325
retrogui_idc_t retrogui_poll_ctls(struct RETROGUI *gui, RETROFLAT_IN_KEY *p_input, struct RETROFLAT_INPUT *input_evt)
Poll for the last clicked control and maintain listboxes and menus.
int16_t retrogui_idc_t
Unique identifying constant number for controls.
Definition retrogui.h:292
ssize_t retrowin_push_win(struct RETROGUI *gui, struct MDATA_VECTOR *win_stack, size_t idc, const char *font_filename, size_t x, size_t y, size_t w, size_t h, uint8_t flags)
Create a new window on the given win_stack.
MERROR_RETVAL retrowin_destroy_win(struct MDATA_VECTOR *win_stack, size_t idc)
Destroy the given window's resources and remove it from the window stack.
MERROR_RETVAL retrofont_load(const char *font_name, MAUG_MHANDLE *p_font_h, uint8_t glyph_h, uint16_t first_glyph, uint16_t glyphs_count)
Load a font for drawing.
A vector of uniformly-sized objects, stored contiguously.
Definition mdata.h:85
Definition retrocon.h:84
Struct passed to retroflat_poll_input() to hold return data.
Definition retroflt.h:814
Definition retrogui.h:430
MAUG_MHANDLE font_h
Font used to draw any attached RETROGUI_CTL.
Definition retrogui.h:456
retrogui_idc_t focus
Unique identifying index for current highlighted RETROGUI_CTL.
Definition retrogui.h:440
Definition retrogui.h:412