Nilorea Library
C utilities for networking, threading, graphics
ex_gui_dictionary.c
1
7#define WIDTH 1280
8#define HEIGHT 800
9
10#include "nilorea/n_common.h"
11#include "nilorea/n_list.h"
12#include "nilorea/n_hash.h"
13#include "nilorea/n_pcre.h"
14#include "nilorea/n_str.h"
15#include "nilorea/n_allegro5.h"
16#include <allegro5/allegro_ttf.h>
17
18/* dictionnaries are from https://www.bragitoff.com/2016/03/english-dictionary-in-csv-format/ */
19
20ALLEGRO_DISPLAY *display = NULL ;
21
22int DONE = 0, /* Flag to check if we are always running */
23 getoptret = 0, /* launch parameter check */
24 log_level = LOG_INFO ; /* default LOG_LEVEL */
25
26ALLEGRO_BITMAP *scr_buf = NULL ;
27
28ALLEGRO_TIMER *fps_timer = NULL ;
29ALLEGRO_TIMER *logic_timer = NULL ;
30
31
32int main( int argc, char *argv[] )
33{
34 set_log_level( log_level );
35
36 n_log( LOG_NOTICE, "%s is starting ...", argv[ 0 ] );
37
38
39 /* allegro 5 + addons loading */
40 if (!al_init())
41 {
42 n_abort("Could not init Allegro.\n");
43 }
44 if (!al_init_image_addon())
45 {
46 n_abort("Unable to initialize image addon\n");
47 }
48 if (!al_init_primitives_addon() )
49 {
50 n_abort("Unable to initialize primitives addon\n");
51 }
52 if( !al_init_font_addon() )
53 {
54 n_abort("Unable to initialize font addon\n");
55 }
56 if( !al_init_ttf_addon() )
57 {
58 n_abort("Unable to initialize ttf_font addon\n");
59 }
60 if( !al_install_keyboard() )
61 {
62 n_abort("Unable to initialize keyboard handler\n");
63 }
64 if( !al_install_mouse())
65 {
66 n_abort("Unable to initialize mouse handler\n");
67 }
68 ALLEGRO_EVENT_QUEUE *event_queue = NULL;
69
70 event_queue = al_create_event_queue();
71 if(!event_queue)
72 {
73 fprintf(stderr, "failed to create event_queue!\n");
74 al_destroy_display(display);
75 return -1;
76 }
77
78 char ver_str[ 128 ] = "" ;
79 while( ( getoptret = getopt( argc, argv, "hvV:L:" ) ) != EOF )
80 {
81 switch( getoptret )
82 {
83 case 'h':
84 n_log( LOG_NOTICE, "\n %s -h help -v version -V LOG_LEVEL (NOLOG,INFO,NOTICE,ERR,DEBUG) -L OPT_LOG_FILE\n", argv[ 0 ] );
85 exit( TRUE );
86 case 'v':
87 sprintf( ver_str, "%s %s", __DATE__, __TIME__ );
88 exit( TRUE );
89 break ;
90 case 'V':
91 if( !strncmp( "INFO", optarg, 6 ) )
92 {
93 log_level = LOG_INFO ;
94 }
95 else
96 {
97 if( !strncmp( "NOTICE", optarg, 7 ) )
98 {
99 log_level = LOG_NOTICE ;
100 }
101 else
102 {
103 if( !strncmp( "ERR", optarg, 5 ) )
104 {
105 log_level = LOG_ERR ;
106 }
107 else
108 {
109 if( !strncmp( "DEBUG", optarg, 5 ) )
110 {
111 log_level = LOG_DEBUG ;
112 }
113 else
114 {
115 n_log( LOG_ERR, "%s is not a valid log level\n", optarg );
116 exit( FALSE );
117 }
118 }
119 }
120 }
121 n_log( LOG_NOTICE, "LOG LEVEL UP TO: %d\n", log_level );
122 set_log_level( log_level );
123 break;
124 case 'L' :
125 n_log( LOG_NOTICE, "LOG FILE: %s\n", optarg );
126 set_log_file( optarg );
127 break ;
128 case '?' :
129 {
130 switch( optopt )
131 {
132 case 'V' :
133 n_log( LOG_ERR, "\nPlease specify a log level after -V. \nAvailable values: NOLOG,VERBOSE,NOTICE,ERROR,DEBUG\n\n" );
134 break;
135 case 'L' :
136 n_log( LOG_ERR, "\nPlease specify a log file after -L\n" );
137 default:
138 break;
139 }
140 }
141 __attribute__ ((fallthrough));
142 default:
143 n_log( LOG_ERR, "\n %s -h help -v version -V DEBUGLEVEL (NOLOG,VERBOSE,NOTICE,ERROR,DEBUG) -L logfile\n", argv[ 0 ] );
144 exit( FALSE );
145 }
146 }
147
148 double fps = 60.0 ;
149 double fps_delta_time = 1.0 / fps ;
150 double logic_delta_time = 1.0 / (5.0 * fps);
151
152 fps_timer = al_create_timer( fps_delta_time );
153 logic_timer = al_create_timer( logic_delta_time );
154
155 al_set_new_display_flags( ALLEGRO_OPENGL|ALLEGRO_WINDOWED );
156 display = al_create_display( WIDTH, HEIGHT );
157 if( !display )
158 {
159 n_abort("Unable to create display\n");
160 }
161 al_set_window_title( display, argv[ 0 ] );
162
163 al_set_new_bitmap_flags( ALLEGRO_VIDEO_BITMAP );
164
165 DONE = 0 ;
166
167 enum APP_KEYS
168 {
169 KEY_ESC
170 };
171 int key[ 1 ] = {false};
172
173 al_register_event_source(event_queue, al_get_display_event_source(display));
174 al_start_timer( fps_timer );
175 al_start_timer( logic_timer );
176 al_register_event_source(event_queue, al_get_timer_event_source(fps_timer));
177 al_register_event_source(event_queue, al_get_timer_event_source(logic_timer));
178
179 al_register_event_source(event_queue, al_get_keyboard_event_source());
180 al_register_event_source(event_queue, al_get_mouse_event_source());
181
182 ALLEGRO_FONT *font = NULL ;
183 font = al_load_font( "DATAS/2Dumb.ttf", 24 , 0 );
184 if (! font )
185 {
186 n_log( LOG_ERR, "Unable to load DATAS/2Dumb.ttf" );
187 exit( 1 );
188 }
189
190 ALLEGRO_BITMAP *scrbuf = al_create_bitmap( WIDTH, HEIGHT );
191
192 al_hide_mouse_cursor(display);
193
194 int mx = 0, my = 0, mz = 0 ;
195 int mouse_b1 = 0, mouse_b2 = 0 ;
196 int do_draw = 0, do_logic = 0 ;
197
199 typedef struct DICTIONARY_DEFINITION
200 {
202 char *type ;
204 char *definition ;
205 } DICTIONARY_DEFINITION;
206
208 typedef struct DICTIONARY_ENTRY
209 {
211 char *key ;
213 LIST *definitions ;
214 } DICTIONARY_ENTRY ;
215
216 void free_entry_def( void *entry_def )
217 {
218 DICTIONARY_DEFINITION *def = (DICTIONARY_DEFINITION *)entry_def;
219 FreeNoLog( def -> type );
220 FreeNoLog( def -> definition );
221 FreeNoLog( def );
222 }
223 void free_entry( void *entry_ptr )
224 {
225 DICTIONARY_ENTRY *entry = (DICTIONARY_ENTRY *)entry_ptr;
226 list_destroy( &entry -> definitions );
227 FreeNoLog( entry -> key );
228 FreeNoLog( entry );
229 }
230
231 /* Load dictionaries */
232 HASH_TABLE *dictionary = new_ht_trie( 256 , 32 );
233 FILE *dict_file = fopen( "DATAS/dictionary.csv" , "r" );
234 char read_buf[ 16384 ] = "" ;
235 char *entry_key = NULL ;
236 char *type = NULL ;
237 char *definition = NULL ;
238 N_PCRE *dico_regexp = npcre_new ( "\"(.*)\",\"(.*)\",\"(.*)\"" , 99 , 0 );
239
240 while( fgets( read_buf , 16384 , dict_file ) )
241 {
242 if( npcre_match( read_buf , dico_regexp ) )
243 {
244 entry_key = strdup( _str( dico_regexp -> match_list[ 1 ] ) );
245 type = strdup( _str( dico_regexp -> match_list[ 2 ] ) );
246 definition = strdup( _str( dico_regexp -> match_list[ 3 ] ) );
247
248 n_log( LOG_DEBUG , "matched %s , %s , %s" , entry_key , type , definition );
249
250 DICTIONARY_ENTRY *entry = NULL ;
251 DICTIONARY_DEFINITION *entry_def = NULL ;
252
253 if( ht_get_ptr( dictionary , entry_key , (void **)&entry ) == TRUE )
254 {
255 Malloc( entry_def , DICTIONARY_DEFINITION , 1 );
256 entry_def -> type = strdup( type );
257 entry_def -> definition = strdup( definition );
258 list_push( entry -> definitions , entry_def , &free_entry_def );
259 }
260 else
261 {
262 Malloc( entry , DICTIONARY_ENTRY , 1 );
263
264 entry -> definitions = new_generic_list( 0 );
265 entry -> key = strdup( entry_key );
266
267 Malloc( entry_def , DICTIONARY_DEFINITION , 1 );
268
269 entry_def -> type = strdup( type );
270 entry_def -> definition = strdup( definition );
271
272 list_push( entry -> definitions , entry_def , &free_entry_def );
273
274 ht_put_ptr( dictionary , entry_key , entry , &free_entry );
275 }
276 FreeNoLog( entry_key );
277 FreeNoLog( type );
278 FreeNoLog( definition );
279
280 npcre_clean_match( dico_regexp );
281 }
282 }
283 fclose( dict_file );
284
285 ALLEGRO_USTR *keyboard_buffer = al_ustr_new( "" );
286 LIST *completion = NULL ;
287 int key_pressed = 0 ;
288 n_log( LOG_NOTICE , "Starting main loop" );
289
290 al_clear_keyboard_state( NULL );
291 al_flush_event_queue( event_queue );
292
293 int max_results = 100 ;
294 completion = ht_get_completion_list( dictionary , al_cstr( keyboard_buffer ) , max_results );
295
296 do
297 {
298 do
299 {
300 ALLEGRO_EVENT ev ;
301
302 al_wait_for_event(event_queue, &ev);
303
304 if(ev.type == ALLEGRO_EVENT_KEY_DOWN)
305 {
306 switch(ev.keyboard.keycode)
307 {
308 case ALLEGRO_KEY_ESCAPE:
309 key[KEY_ESC] = 1 ;
310 break;
311 default:
312 break;
313 }
314 }
315 else if(ev.type == ALLEGRO_EVENT_KEY_UP)
316 {
317 switch(ev.keyboard.keycode)
318 {
319 case ALLEGRO_KEY_ESCAPE:
320 key[KEY_ESC] = 0 ;
321 break;
322 default:
323 break;
324 }
325 }
326 else if( ev.type == ALLEGRO_EVENT_TIMER )
327 {
328 if( al_get_timer_event_source( fps_timer ) == ev.any.source )
329 {
330 do_draw = 1 ;
331 }
332 else if( al_get_timer_event_source( logic_timer ) == ev.any.source )
333 {
334 do_logic = 1;
335 }
336 }
337 else if( ev.type == ALLEGRO_EVENT_MOUSE_AXES )
338 {
339 mx = ev.mouse.x;
340 my = ev.mouse.y;
341 mz -= ev.mouse.dz;
342 if( mz < 0 ) mz = 0 ;
343 }
344 else if( ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN )
345 {
346 if( ev.mouse.button == 1 )
347 mouse_b1 = 1 ;
348 if( ev.mouse.button == 2 )
349 mouse_b2 = 1 ;
350 }
351 else if( ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP )
352 {
353 if( ev.mouse.button == 1 )
354 mouse_b1 = 0 ;
355 if( ev.mouse.button == 2 )
356 mouse_b2 = 0 ;
357 }
358 else if( ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_IN || ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_OUT )
359 {
360 al_clear_keyboard_state( display );
361 al_flush_event_queue( event_queue );
362 }
363 else
364 {
365 /* Processing inputs */
366 if( get_keyboard( keyboard_buffer , ev ) == TRUE )
367 key_pressed = 1 ;
368 }
369 }while( !al_is_event_queue_empty( event_queue) );
370
371 if( do_logic == 1 )
372 {
373 if( key_pressed == 1 )
374 {
375 key_pressed = 0 ;
376 mz = 0 ;
377 if( completion )
378 {
379 list_destroy( &completion );
380 }
381 completion = ht_get_completion_list( dictionary , al_cstr( keyboard_buffer ) , max_results );
382 }
383
384 if( mouse_b1==1 )
385 {
386 if( completion )
387 {
388 int skip_entry = mz ;
389 if( skip_entry >= completion -> nb_items )
390 {
391 skip_entry = completion -> nb_items - 1 ;
392 mz = skip_entry ;
393 }
394 char *key = NULL ;
395 list_foreach( node , completion )
396 {
397 skip_entry -- ;
398 key = (char *)node -> ptr ;
399
400 if( skip_entry < 0 )
401 {
402 break ;
403 }
404 }
405 al_ustr_free( keyboard_buffer );
406 keyboard_buffer = al_ustr_new( key );
407 }
408 completion = ht_get_completion_list( dictionary , al_cstr( keyboard_buffer ) , max_results );
409 mz = 0 ;
410 mouse_b1 = 0 ;
411 }
412 if( mouse_b2==1 )
413 {
414 int pos = (int)al_ustr_size( keyboard_buffer );
415 if( al_ustr_prev( keyboard_buffer , &pos ) )
416 {
417 al_ustr_truncate( keyboard_buffer , pos );
418 }
419 completion = ht_get_completion_list( dictionary , al_cstr( keyboard_buffer ) , max_results );
420 mouse_b2 = 0 ;
421 }
422
423 do_logic = 0 ;
424 }
425
426 if( do_draw == 1 )
427 {
428 static int last_time = 0 ;
429
430 al_acknowledge_resize( display );
431 //int w = al_get_display_width( display );
432 //int h = al_get_display_height( display );
433
434 al_set_target_bitmap( scrbuf );
435 al_clear_to_color( al_map_rgba( 0, 0, 0, 255 ) );
436
437 last_time -= 1000000/30.0 ;
438 static int length = 0 ;
439 if( last_time < 0 )
440 {
441 last_time = 250000 ;
442 }
443 else
444 {
445 length = al_get_text_width( font , al_cstr( keyboard_buffer ) );
446 al_draw_filled_rectangle( WIDTH / 2 + ( length + 6 ) / 2 , 50 ,
447 WIDTH / 2 + ( length + 6 ) / 2 + 15 , 70 ,
448 al_map_rgb( 0 , 128 , 0 ) );
449 }
450 int input_size = al_get_text_width( font , al_cstr( keyboard_buffer ) );
451 al_draw_text( font , al_map_rgb( 0 , 255 , 0 ) , (WIDTH / 2) - (input_size / 2), 50 , ALLEGRO_ALIGN_LEFT , al_cstr( keyboard_buffer ) );
452
453 DICTIONARY_ENTRY *entry = NULL ;
454
455 if( completion )
456 {
457 int pos_x = (WIDTH / 2) + (input_size / 2) + 50 ;
458 int pos_y = 50 ;
459 int skip_entry = mz ;
460 if( skip_entry >= completion -> nb_items )
461 {
462 skip_entry = completion -> nb_items - 1 ;
463 mz = skip_entry ;
464 }
465
466 list_foreach( node , completion )
467 {
468 if( skip_entry > 0 )
469 {
470 skip_entry -- ;
471 continue ;
472 }
473 char *key = (char *)node -> ptr ;
474 al_draw_text( font , al_map_rgb( 230 , 230 , 0 ) , pos_x , pos_y , ALLEGRO_ALIGN_LEFT , key );
475 pos_y += 35 ;
476 }
477 }
478
479 if( ht_get_ptr( dictionary , al_cstr( keyboard_buffer ) , (void **)&entry ) == TRUE )
480 {
481 int vertical_it = 0 ;
482 list_foreach( node , entry -> definitions )
483 {
484 DICTIONARY_DEFINITION *definition = (DICTIONARY_DEFINITION *)node -> ptr ;
485 al_draw_multiline_textf( font , al_map_rgb( 0 , 255 , 0 ) , 10 , 100 + vertical_it , WIDTH - 10 , 20 , ALLEGRO_ALIGN_LEFT , "%s : %s" , definition -> type , definition -> definition );
486 vertical_it += 50 ;
487 }
488 }
489
490 al_set_target_bitmap( al_get_backbuffer( display ) );
491 al_draw_bitmap( scrbuf, 0, 0, 0 );
492
493 /* mouse pointer */
494 al_draw_line( mx - 5, my, mx + 5, my, al_map_rgb( 255, 0, 0 ), 1 );
495 al_draw_line( mx, my + 5, mx, my - 5, al_map_rgb( 255, 0, 0 ), 1 );
496
497 al_flip_display();
498 do_draw = 0 ;
499 }
500
501 }
502 while( !key[KEY_ESC] && !DONE );
503
504 destroy_ht( &dictionary );
505
506 return 0;
507}
int get_keyboard(ALLEGRO_USTR *str, ALLEGRO_EVENT event)
update a keyboard buffer from an event
Definition: n_allegro5.c:19
#define FreeNoLog(__ptr)
Free Handler without log.
Definition: n_common.h:268
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition: n_common.h:183
#define _str(__PTR)
define true
Definition: n_common.h:172
void n_abort(char const *format,...)
abort program with a text
Definition: n_common.c:38
int ht_get_ptr(HASH_TABLE *table, const char *key, void **val)
get pointer at 'key' from 'table'
Definition: n_hash.c:2282
int destroy_ht(HASH_TABLE **table)
empty a table and destroy it
Definition: n_hash.c:2448
LIST * ht_get_completion_list(HASH_TABLE *table, const char *keybud, size_t max_results)
get next matching keys in table tree
Definition: n_hash.c:2630
HASH_TABLE * new_ht_trie(size_t alphabet_length, size_t alphabet_offset)
create a TRIE hash table with the alphabet_size, each key value beeing decreased by alphabet_offset
Definition: n_hash.c:2123
int ht_put_ptr(HASH_TABLE *table, const char *key, void *ptr, void(*destructor)(void *ptr))
put a pointer to the string value with given key in the targeted hash table
Definition: n_hash.c:2347
structure of a hash table
Definition: n_hash.h:109
int list_push(LIST *list, void *ptr, void(*destructor)(void *ptr))
Add a pointer to the end of the list.
Definition: n_list.c:244
#define list_foreach(__ITEM_, __LIST_)
ForEach macro helper.
Definition: n_list.h:70
int list_destroy(LIST **list)
Empty and Free a list container.
Definition: n_list.c:603
LIST * new_generic_list(int max_items)
Initialiaze a generic list container to max_items pointers.
Definition: n_list.c:20
Structure of a generic LIST container.
Definition: n_list.h:45
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition: n_log.h:74
#define LOG_DEBUG
debug-level messages
Definition: n_log.h:66
#define LOG_ERR
error conditions
Definition: n_log.h:58
int set_log_file(char *file)
Set the logging to a file instead of stderr.
Definition: n_log.c:135
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL )
Definition: n_log.c:97
#define LOG_NOTICE
normal but significant condition
Definition: n_log.h:62
#define LOG_INFO
informational
Definition: n_log.h:64
N_PCRE * npcre_new(char *str, int max_cap, int flags)
From pcre doc, the flag bits are: PCRE_ANCHORED Force pattern anchoring PCRE_AUTO_CALLOUT Compile aut...
Definition: n_pcre.c:59
int npcre_match(char *str, N_PCRE *pcre)
Return TRUE if str matches regexp, and make captures up to max_cap.
Definition: n_pcre.c:165
int npcre_clean_match(N_PCRE *pcre)
clean the match list of the last capture, if any
Definition: n_pcre.c:144
N_PCRE structure.
Definition: n_pcre.h:29
Allegro5 helpers.
Common headers and low-level hugly functions & define.
Hash functions and table.
List structures and definitions.
PCRE helpers for regex matching.
N_STR and string function declaration.