naev 0.11.5
gui_osd.c
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
5#include <stdlib.h>
6
7#include "naev.h"
10#include "gui_osd.h"
11
12#include "array.h"
13#include "font.h"
14#include "log.h"
15#include "nstring.h"
16#include "opengl.h"
17
21typedef struct OSD_s {
22 unsigned int id;
24 int skip;
25 int hide;
27 char *title;
28 char **titlew;
30 char **msg;
31 char ***items;
33 unsigned int active;
34} OSD_t;
35
36/*
37 * OSD array.
38 */
39static unsigned int osd_idgen = 0;
40static OSD_t *osd_list = NULL;
42/*
43 * Dimensions.
44 */
45static int osd_x = 0;
46static int osd_y = 0;
47static int osd_w = 0;
48static int osd_h = 0;
49static int osd_lines = 0;
50static int osd_rh = 0;
51static int osd_tabLen = 0;
52static int osd_hyphenLen = 0;
53
54/*
55 * Prototypes.
56 */
57static OSD_t *osd_get( unsigned int osd );
58static int osd_free( OSD_t *osd );
59static void osd_calcDimensions (void);
60/* Sort. */
61static int osd_sortCompare( const void * arg1, const void * arg2 );
62static void osd_sort (void);
63static void osd_wordwrap( OSD_t* osd );
64
65static int osd_sortCompare( const void *arg1, const void *arg2 )
66{
67 const OSD_t *osd1, *osd2;
68 int ret, m;
69
70 osd1 = (OSD_t*)arg1;
71 osd2 = (OSD_t*)arg2;
72
73 /* Compare priority. */
74 if (osd1->priority > osd2->priority)
75 return +1;
76 else if (osd1->priority < osd2->priority)
77 return -1;
78
79 /* Compare name. */
80 ret = strcmp( osd1->title, osd2->title );
81 if (ret != 0)
82 return ret;
83
84 /* Compare items. */
85 m = MIN(array_size(osd1->items), array_size(osd2->items));
86 for (int i=0; i<m; i++) {
87 ret = strcmp( osd1->msg[i], osd2->msg[i] );
88 if (ret != 0)
89 return ret;
90 }
91
92 /* Compare on length. */
93 if (array_size(osd1->items) > array_size(osd2->items))
94 return +1;
95 if (array_size(osd1->items) < array_size(osd2->items))
96 return -1;
97
98 /* Compare ID. */
99 if (osd1->id > osd2->id)
100 return +1;
101 else if (osd1->id < osd2->id)
102 return -1;
103 return 0;
104}
105
109static void osd_sort (void)
110{
111 qsort( osd_list, array_size(osd_list), sizeof(OSD_t), osd_sortCompare );
112}
113
122unsigned int osd_create( const char *title, int nitems, const char **items, int priority )
123{
124 int id;
125 OSD_t *osd;
126
127 /* Create. */
128 if (osd_list == NULL)
129 osd_list = array_create( OSD_t );
130 osd = &array_grow( &osd_list );
131 memset( osd, 0, sizeof(OSD_t) );
132 osd->id = id = ++osd_idgen;
133 osd->active = 0;
134
135 /* Copy text. */
136 osd->title = strdup(title);
137 osd->priority = priority;
138 osd->msg = array_create_size( char*, nitems );
139 osd->items = array_create_size( char**, nitems );
140 osd->titlew = array_create( char* );
141 for (int i=0; i<nitems; i++) {
142 array_push_back( &osd->msg, strdup( items[i] ) );
143 array_push_back( &osd->items, array_create(char*) );
144 }
145
146 osd_sort(); /* THIS INVALIDATES THE osd POINTER. */
147 osd_calcDimensions();
148
149 return id;
150}
151
155static void osd_wordwrap( OSD_t* osd )
156{
158 char title[STRMAX_SHORT]; /* Needs to be in the scope of the entire function as it is used in gl_printLineIteratorNext indirectly. */
159
160 /* Do title. */
161 for (int i=0; i<array_size(osd->titlew); i++)
162 free(osd->titlew[i]);
163 array_resize( &osd->titlew, 0 );
164
165 /* Handle the case same mission is repeated. */
166 if (osd->duplicates > 0) {
167 snprintf( title, sizeof(title), _("%s #b(%dx)#0"), osd->title, osd->duplicates+1 );
168 gl_printLineIteratorInit( &iter, &gl_smallFont, title, osd_w );
169 }
170 else
171 gl_printLineIteratorInit( &iter, &gl_smallFont, osd->title, osd_w );
172
173 /* Figure out the length. */
174 while (gl_printLineIteratorNext( &iter )) {
175 /* Copy text over. */
176 int chunk_len = iter.l_end - iter.l_begin + 1;
177 char *chunk = malloc( chunk_len );
178 snprintf( chunk, chunk_len, "%s", &iter.text[iter.l_begin] );
179 array_push_back( &osd->titlew, chunk );
180 }
181
182 /* Do items. */
183 for (int i=0; i<array_size(osd->items); i++) {
184 int msg_len, w, has_tab;
185 const char *chunk_fmt;
186 for (int l=0; l<array_size(osd->items[i]); l++)
187 free(osd->items[i][l]);
188 array_resize( &osd->items[i], 0 );
189
190 msg_len = strlen(osd->msg[i]);
191 if (msg_len == 0)
192 continue;
193
194 /* Test if tabbed. */
195 has_tab = !!(osd->msg[i][0] == '\t');
196 w = osd_w - (has_tab ? osd_tabLen : osd_hyphenLen);
197 gl_printLineIteratorInit( &iter, &gl_smallFont, &osd->msg[i][has_tab], w );
198 chunk_fmt = has_tab ? " %s" : "- %s";
199
200 while (gl_printLineIteratorNext( &iter )) {
201 /* Copy text over. */
202 int chunk_len = iter.l_end - iter.l_begin + strlen( chunk_fmt ) - 1;
203 char *chunk = malloc( chunk_len );
204 snprintf( chunk, chunk_len, chunk_fmt, &iter.text[iter.l_begin] );
205 array_push_back( &osd->items[i], chunk );
206 chunk_fmt = has_tab ? " %s" : "%s";
207 iter.width = has_tab ? osd_w - osd_tabLen - osd_hyphenLen : osd_w - osd_hyphenLen;
208 }
209 }
210}
211
217static OSD_t *osd_get( unsigned int osd )
218{
219 for (int i=0; i<array_size(osd_list); i++) {
220 OSD_t *ll = &osd_list[i];
221 if (ll->id == osd)
222 return ll;
223 }
224 WARN(_("OSD '%d' not found."), osd);
225 return NULL;
226}
227
231static int osd_free( OSD_t *osd )
232{
233 free(osd->title);
234 for (int i=0; i<array_size(osd->items); i++) {
235 free( osd->msg[i] );
236 for (int j=0; j<array_size(osd->items[i]); j++)
237 free(osd->items[i][j]);
238 array_free(osd->items[i]);
239 }
240 array_free(osd->msg);
241 array_free(osd->items);
242 for (int i=0; i<array_size(osd->titlew); i++)
243 free(osd->titlew[i]);
244 array_free(osd->titlew);
245
246 return 0;
247}
248
254int osd_destroy( unsigned int osd )
255{
256 for (int i=0; i<array_size( osd_list ); i++) {
257 OSD_t *ll = &osd_list[i];
258 if (ll->id != osd)
259 continue;
260
261 /* Clean up. */
262 osd_free( &osd_list[i] );
263
264 /* Remove. */
265 array_erase( &osd_list, &osd_list[i], &osd_list[i+1] );
266
267 /* Recalculate dimensions. */
268 osd_calcDimensions();
269
270 /* Remove the OSD, if empty. */
271 if (array_size(osd_list) == 0)
272 osd_exit();
273
274 /* Done here. */
275 return 0;
276 }
277
278 WARN(_("OSD '%u' not found to destroy."), osd );
279 return 0;
280}
281
289int osd_active( unsigned int osd, int msg )
290{
291 OSD_t *o = osd_get(osd);
292 if (o == NULL)
293 return -1;
294
295 if ((msg < 0) || (msg >= array_size(o->items))) {
296 WARN(_("OSD '%s' only has %d items (requested %d)"), o->title, array_size(o->items), msg );
297 return -1;
298 }
299
300 o->active = msg;
301 osd_calcDimensions();
302 return 0;
303}
304
311int osd_getActive( unsigned int osd )
312{
313 OSD_t *o = osd_get(osd);
314 if (o == NULL)
315 return -1;
316
317 return o->active;
318}
319
328int osd_setup( int x, int y, int w, int h )
329{
330 /* Set offsets. */
331 osd_x = x;
332 osd_y = y;
333 osd_w = w;
334 osd_lines = h / (gl_smallFont.h+5);
335 osd_h = h - h % (gl_smallFont.h+5);
336
337 /* Calculate some font things. */
338 osd_tabLen = gl_printWidthRaw( &gl_smallFont, " " );
339 osd_hyphenLen = gl_printWidthRaw( &gl_smallFont, "- " );
340
341 osd_calcDimensions();
342
343 return 0;
344}
345
349void osd_exit (void)
350{
351 for (int i=0; i<array_size(osd_list); i++) {
352 OSD_t *ll = &osd_list[i];
353 osd_free( ll );
354 }
355
356 array_free( osd_list );
357 osd_list = NULL;
358}
359
363void osd_render (void)
364{
365 double p;
366 int l;
367
368 /* Nothing to render. */
369 if (osd_list == NULL)
370 return;
371
372 /* Background. */
373 gl_renderRect( osd_x-5., osd_y-(osd_rh+5.), osd_w+10., osd_rh+10, &cBlackHilight );
374
375 /* Render each thingy. */
376 p = osd_y-gl_smallFont.h;
377 l = 0;
378 for (int k=0; k<array_size(osd_list); k++) {
379 int x, w;
380 OSD_t *ll = &osd_list[k];
381
382 if (ll->skip)
383 continue;
384
385 x = osd_x;
386 w = osd_w;
387
388 /* Print title. */
389 for (int i=0; i<array_size(ll->titlew); i++) {
390 gl_printMaxRaw( &gl_smallFont, w, x, p, NULL, -1., ll->titlew[i]);
391 p -= gl_smallFont.h + 5.;
392 l++;
393 }
394 if (l >= osd_lines)
395 return;
396
397 /* Print items. */
398 for (int i=ll->active; i<array_size(ll->items); i++) {
399 const glColour *c = (i == (int)ll->active) ? &cFontWhite : &cFontGrey;
400 x = osd_x;
401 for (int j=0; j<array_size(ll->items[i]); j++) {
402 gl_printRaw( &gl_smallFont, x, p, c, -1., ll->items[i][j] );
403 if (j==0)
404 x = osd_x + osd_hyphenLen;
405 p -= gl_smallFont.h + 5.;
406 l++;
407 if (l >= osd_lines)
408 return;
409 }
410 }
411 }
412}
413
417static void osd_calcDimensions (void)
418{
419 double len;
420
421 /* Nothing to render. */
422 if (osd_list == NULL)
423 return;
424
425 /* Reset skips. */
426 for (int k=0; k<array_size(osd_list); k++) {
427 OSD_t *ll = &osd_list[k];
428 ll->skip = ll->hide;
429 ll->duplicates = 0;
430 }
431
432 /* Get duplicates. */
433 for (int k=0; k<array_size(osd_list); k++) {
434 int duplicates;
435 OSD_t *ll = &osd_list[k];
436
437 if (ll->skip)
438 continue;
439
440 /* Check how many duplicates we have, mark duplicates for ignoring */
441 duplicates = 0;
442 for (int m=k+1; m<array_size(osd_list); m++) {
443 OSD_t *lm = &osd_list[m];
444
445 if ((strcmp(lm->title, ll->title) == 0) &&
446 (array_size(lm->items) == array_size(ll->items)) &&
447 (lm->active == ll->active)) {
448 int is_duplicate = 1;
449 for (int i=lm->active; i<array_size(lm->items); i++) {
450 if (array_size(lm->items[i]) == array_size(ll->items[i])) {
451 for (int j=0; j<array_size(lm->items[i]); j++) {
452 if (strcmp(lm->items[i][j], ll->items[i][j]) != 0 ) {
453 is_duplicate = 0;
454 break;
455 }
456 }
457 } else {
458 is_duplicate = 0;
459 }
460 if (!is_duplicate)
461 break;
462 }
463 if (is_duplicate) {
464 duplicates++;
465 lm->skip = 1;
466 }
467 }
468 }
469 ll->duplicates = duplicates;
470 }
471
472 /* Compute total length. */
473 len = 0;
474 for (int k=0; k<array_size(osd_list); k++) {
475 OSD_t *ll = &osd_list[k];
476
477 if (ll->skip)
478 continue;
479
480 /* Wordwrap. */
481 osd_wordwrap( ll );
482
483 /* Print title. */
484 for (int i=0; i<array_size(ll->titlew); i++)
485 len += gl_smallFont.h + 5.;
486
487 /* Print items. */
488 for (int i=ll->active; i<array_size(ll->items); i++)
489 for (int j=0; j<array_size(ll->items[i]); j++)
490 len += gl_smallFont.h + 5.;
491 }
492 osd_rh = MIN( len, osd_h );
493}
494
501char *osd_getTitle( unsigned int osd )
502{
503 OSD_t *o = osd_get(osd);
504 if (o == NULL)
505 return NULL;
506
507 return o->title;
508}
509
516char **osd_getItems( unsigned int osd )
517{
518 OSD_t *o = osd_get(osd);
519 if (o == NULL)
520 return NULL;
521 return o->msg;
522}
523
530int osd_setHide( unsigned int osd, int state )
531{
532 OSD_t *o = osd_get(osd);
533 if (o == NULL)
534 return -1;
535
536 o->hide = state;
537 osd_calcDimensions();
538 return 0;
539}
540
547int osd_getHide( unsigned int osd )
548{
549 OSD_t *o = osd_get(osd);
550 if (o == NULL)
551 return -1;
552 return o->hide;
553}
554
561int osd_setPriority( unsigned int osd, int priority )
562{
563 OSD_t *o = osd_get(osd);
564 if (o == NULL)
565 return -1;
566
567 o->priority = priority;
568 osd_sort();
569 osd_calcDimensions();
570 return 0;
571}
572
579int osd_getPriority( unsigned int osd )
580{
581 OSD_t *o = osd_get(osd);
582 if (o == NULL)
583 return -1;
584 return o->priority;
585}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:158
#define array_resize(ptr_array, new_size)
Resizes the array to accomodate new_size elements.
Definition array.h:112
#define array_create_size(basic_type, capacity)
Creates a new dynamic array of ‘basic_type’ with an initial capacity.
Definition array.h:102
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
Definition array.h:140
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:168
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition array.h:119
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition array.h:129
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
int gl_printLineIteratorNext(glPrintLineIterator *iter)
Updates iter with the next line's information.
Definition font.c:526
glFont gl_smallFont
Definition font.c:154
void gl_printRaw(const glFont *ft_font, double x, double y, const glColour *c, double outlineR, const char *text)
Prints text on screen.
Definition font.c:617
int gl_printWidthRaw(const glFont *ft_font, const char *text)
Gets the width that it would take to print some text.
Definition font.c:961
void gl_printLineIteratorInit(glPrintLineIterator *iter, const glFont *ft_font, const char *text, int width)
Initialize an iterator object for breaking text into lines.
Definition font.c:506
int gl_printMaxRaw(const glFont *ft_font, const int max, double x, double y, const glColour *c, double outlineR, const char *text)
Behaves like gl_printRaw but stops displaying text after a certain distance.
Definition font.c:721
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition naev.h:40
void gl_renderRect(double x, double y, double w, double h, const glColour *c)
Renders a rectangle.
static const double c[]
Definition rng.c:264
On Screen Display element.
Definition gui_osd.c:21
char *** items
Definition gui_osd.c:31
unsigned int active
Definition gui_osd.c:33
char * title
Definition gui_osd.c:27
int priority
Definition gui_osd.c:23
char ** msg
Definition gui_osd.c:30
int hide
Definition gui_osd.c:25
int duplicates
Definition gui_osd.c:26
char ** titlew
Definition gui_osd.c:28
unsigned int id
Definition gui_osd.c:22
int skip
Definition gui_osd.c:24
int h
Definition font.h:18
The state of a line iteration. This matches the process of rendering text into an on-screen box: An e...
Definition font.h:40
const char * text
Definition font.h:41