naev 0.10.4
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;
26 char *title;
27 char **titlew;
29 char **msg;
30 char ***items;
32 unsigned int active;
33} OSD_t;
34
35/*
36 * OSD array.
37 */
38static unsigned int osd_idgen = 0;
39static OSD_t *osd_list = NULL;
41/*
42 * Dimensions.
43 */
44static int osd_x = 0;
45static int osd_y = 0;
46static int osd_w = 0;
47static int osd_h = 0;
48static int osd_lines = 0;
49static int osd_rh = 0;
50static int osd_tabLen = 0;
51static int osd_hyphenLen = 0;
52
53/*
54 * Prototypes.
55 */
56static OSD_t *osd_get( unsigned int osd );
57static int osd_free( OSD_t *osd );
58static void osd_calcDimensions (void);
59/* Sort. */
60static int osd_sortCompare( const void * arg1, const void * arg2 );
61static void osd_sort (void);
62static void osd_wordwrap( OSD_t* osd );
63
64static int osd_sortCompare( const void *arg1, const void *arg2 )
65{
66 const OSD_t *osd1, *osd2;
67 int ret, m;
68
69 osd1 = (OSD_t*)arg1;
70 osd2 = (OSD_t*)arg2;
71
72 /* Compare priority. */
73 if (osd1->priority > osd2->priority)
74 return +1;
75 else if (osd1->priority < osd2->priority)
76 return -1;
77
78 /* Compare name. */
79 ret = strcmp( osd1->title, osd2->title );
80 if (ret != 0)
81 return ret;
82
83 /* Compare items. */
84 m = MIN(array_size(osd1->items), array_size(osd2->items));
85 for (int i=0; i<m; i++) {
86 ret = strcmp( osd1->msg[i], osd2->msg[i] );
87 if (ret != 0)
88 return ret;
89 }
90
91 /* Compare on length. */
92 if (array_size(osd1->items) > array_size(osd2->items))
93 return +1;
94 if (array_size(osd1->items) < array_size(osd2->items))
95 return -1;
96
97 /* Compare ID. */
98 if (osd1->id > osd2->id)
99 return +1;
100 else if (osd1->id < osd2->id)
101 return -1;
102 return 0;
103}
104
108static void osd_sort (void)
109{
110 qsort( osd_list, array_size(osd_list), sizeof(OSD_t), osd_sortCompare );
111}
112
121unsigned int osd_create( const char *title, int nitems, const char **items, int priority )
122{
123 int id;
124 OSD_t *osd;
125
126 /* Create. */
127 if (osd_list == NULL)
128 osd_list = array_create( OSD_t );
129 osd = &array_grow( &osd_list );
130 memset( osd, 0, sizeof(OSD_t) );
131 osd->id = id = ++osd_idgen;
132 osd->active = 0;
133
134 /* Copy text. */
135 osd->title = strdup(title);
136 osd->priority = priority;
137 osd->msg = array_create_size( char*, nitems );
138 osd->items = array_create_size( char**, nitems );
139 osd->titlew = array_create( char* );
140 for (int i=0; i<nitems; i++) {
141 array_push_back( &osd->msg, strdup( items[i] ) );
142 array_push_back( &osd->items, array_create(char*) );
143 }
144
145 osd_wordwrap( osd );
146 osd_sort(); /* THIS INVALIDATES THE osd POINTER. */
147 osd_calcDimensions();
148
149 return id;
150}
151
155void osd_wordwrap( OSD_t* osd )
156{
158
159 /* Do title. */
160 for (int i=0; i<array_size(osd->titlew); i++)
161 free(osd->titlew[i]);
162 array_resize( &osd->titlew, 0 );
163 gl_printLineIteratorInit( &iter, &gl_smallFont, osd->title, osd_w );
164 while (gl_printLineIteratorNext( &iter )) {
165 /* Copy text over. */
166 int chunk_len = iter.l_end - iter.l_begin + 1;
167 char *chunk = malloc( chunk_len );
168 snprintf( chunk, chunk_len, "%s", &iter.text[iter.l_begin] );
169 array_push_back( &osd->titlew, chunk );
170 }
171
172 /* Do items. */
173 for (int i=0; i<array_size(osd->items); i++) {
174 int msg_len, w, has_tab;
175 const char *chunk_fmt;
176 for (int l=0; l<array_size(osd->items[i]); l++)
177 free(osd->items[i][l]);
178 array_resize( &osd->items[i], 0 );
179
180 msg_len = strlen(osd->msg[i]);
181 if (msg_len == 0)
182 continue;
183
184 /* Test if tabbed. */
185 has_tab = !!(osd->msg[i][0] == '\t');
186 w = osd_w - (has_tab ? osd_tabLen : osd_hyphenLen);
187 gl_printLineIteratorInit( &iter, &gl_smallFont, &osd->msg[i][has_tab], w );
188 chunk_fmt = has_tab ? " %s" : "- %s";
189
190 while (gl_printLineIteratorNext( &iter )) {
191 /* Copy text over. */
192 int chunk_len = iter.l_end - iter.l_begin + strlen( chunk_fmt ) - 1;
193 char *chunk = malloc( chunk_len );
194 snprintf( chunk, chunk_len, chunk_fmt, &iter.text[iter.l_begin] );
195 array_push_back( &osd->items[i], chunk );
196 chunk_fmt = has_tab ? " %s" : "%s";
197 iter.width = has_tab ? osd_w - osd_tabLen - osd_hyphenLen : osd_w - osd_hyphenLen;
198 }
199 }
200}
201
207static OSD_t *osd_get( unsigned int osd )
208{
209 for (int i=0; i<array_size(osd_list); i++) {
210 OSD_t *ll = &osd_list[i];
211 if (ll->id == osd)
212 return ll;
213 }
214 WARN(_("OSD '%d' not found."), osd);
215 return NULL;
216}
217
221static int osd_free( OSD_t *osd )
222{
223 free(osd->title);
224 for (int i=0; i<array_size(osd->items); i++) {
225 free( osd->msg[i] );
226 for (int j=0; j<array_size(osd->items[i]); j++)
227 free(osd->items[i][j]);
228 array_free(osd->items[i]);
229 }
230 array_free(osd->msg);
231 array_free(osd->items);
232 for (int i=0; i<array_size(osd->titlew); i++)
233 free(osd->titlew[i]);
234 array_free(osd->titlew);
235
236 return 0;
237}
238
244int osd_destroy( unsigned int osd )
245{
246 for (int i=0; i<array_size( osd_list ); i++) {
247 OSD_t *ll = &osd_list[i];
248 if (ll->id != osd)
249 continue;
250
251 /* Clean up. */
252 osd_free( &osd_list[i] );
253
254 /* Remove. */
255 array_erase( &osd_list, &osd_list[i], &osd_list[i+1] );
256
257 /* Recalculate dimensions. */
258 osd_calcDimensions();
259
260 /* Remove the OSD, if empty. */
261 if (array_size(osd_list) == 0)
262 osd_exit();
263
264 /* Done here. */
265 return 0;
266 }
267
268 WARN(_("OSD '%u' not found to destroy."), osd );
269 return 0;
270}
271
279int osd_active( unsigned int osd, int msg )
280{
281 OSD_t *o = osd_get(osd);
282 if (o == NULL)
283 return -1;
284
285 if ((msg < 0) || (msg >= array_size(o->items))) {
286 WARN(_("OSD '%s' only has %d items (requested %d)"), o->title, array_size(o->items), msg );
287 return -1;
288 }
289
290 o->active = msg;
291 osd_calcDimensions();
292 return 0;
293}
294
301int osd_getActive( unsigned int osd )
302{
303 OSD_t *o = osd_get(osd);
304 if (o == NULL)
305 return -1;
306
307 return o->active;
308}
309
318int osd_setup( int x, int y, int w, int h )
319{
320 /* Set offsets. */
321 int must_rewrap = (osd_w != w) && (osd_list != NULL);
322 osd_x = x;
323 osd_y = y;
324 osd_w = w;
325 osd_lines = h / (gl_smallFont.h+5);
326 osd_h = h - h % (gl_smallFont.h+5);
327
328 /* Calculate some font things. */
329 osd_tabLen = gl_printWidthRaw( &gl_smallFont, " " );
330 osd_hyphenLen = gl_printWidthRaw( &gl_smallFont, "- " );
331
332 if (must_rewrap)
333 for (int i=0; i<array_size(osd_list); i++)
334 osd_wordwrap( &osd_list[i] );
335 osd_calcDimensions();
336
337 return 0;
338}
339
343void osd_exit (void)
344{
345 for (int i=0; i<array_size(osd_list); i++) {
346 OSD_t *ll = &osd_list[i];
347 osd_free( ll );
348 }
349
350 array_free( osd_list );
351 osd_list = NULL;
352}
353
357void osd_render (void)
358{
359 double p;
360 int l;
361 char title[STRMAX_SHORT];
362
363 /* Nothing to render. */
364 if (osd_list == NULL)
365 return;
366
367 /* Background. */
368 gl_renderRect( osd_x-5., osd_y-(osd_rh+5.), osd_w+10., osd_rh+10, &cBlackHilight );
369
370 /* Render each thingy. */
371 p = osd_y-gl_smallFont.h;
372 l = 0;
373 for (int k=0; k<array_size(osd_list); k++) {
374 int x, w;
375 OSD_t *ll = &osd_list[k];
376
377 if (ll->skip)
378 continue;
379
380 x = osd_x;
381 w = osd_w;
382
383 /* Print title. */
384 for (int i=0; i<array_size(ll->titlew); i++) {
385 if ((ll->duplicates > 0) && (i==array_size(ll->titlew)-1)) {
386 snprintf( title, sizeof(title), "%s #b(%dx)#0", ll->titlew[i], ll->duplicates+1 );
387 gl_printMaxRaw( &gl_smallFont, w, x, p, NULL, -1., title);
388 }
389 else
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 = 0;
429 ll->duplicates = 0;
430 }
431
432 /* Render each thingy. */
433 len = 0;
434 for (int k=0; k<array_size(osd_list); k++) {
435 int duplicates;
436 OSD_t *ll = &osd_list[k];
437
438 if (ll->skip)
439 continue;
440
441 /* Check how many duplicates we have, mark duplicates for ignoring */
442 duplicates = 0;
443 for (int m=k+1; m<array_size(osd_list); m++) {
444 OSD_t *lm = &osd_list[m];
445
446 if ((strcmp(lm->title, ll->title) == 0) &&
447 (array_size(lm->items) == array_size(ll->items)) &&
448 (lm->active == ll->active)) {
449 int is_duplicate = 1;
450 for (int i=lm->active; i<array_size(lm->items); i++) {
451 if (array_size(lm->items[i]) == array_size(ll->items[i])) {
452 for (int j=0; j<array_size(lm->items[i]); j++) {
453 if (strcmp(lm->items[i][j], ll->items[i][j]) != 0 ) {
454 is_duplicate = 0;
455 break;
456 }
457 }
458 } else {
459 is_duplicate = 0;
460 }
461 if (!is_duplicate)
462 break;
463 }
464 if (is_duplicate) {
465 duplicates++;
466 lm->skip = 1;
467 }
468 }
469 }
470 ll->duplicates = duplicates;
471
472 /* Print title. */
473 for (int i=0; i<array_size(ll->titlew); i++)
474 len += gl_smallFont.h + 5.;
475
476 /* Print items. */
477 for (int i=ll->active; i<array_size(ll->items); i++)
478 for (int j=0; j<array_size(ll->items[i]); j++)
479 len += gl_smallFont.h + 5.;
480 }
481 osd_rh = MIN( len, osd_h );
482}
483
490char *osd_getTitle( unsigned int osd )
491{
492 OSD_t *o = osd_get(osd);
493 if (o == NULL)
494 return NULL;
495
496 return o->title;
497}
498
505char **osd_getItems( unsigned int osd )
506{
507 OSD_t *o = osd_get(osd);
508 if (o == NULL)
509 return NULL;
510 return o->msg;
511}
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:525
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:616
int gl_printWidthRaw(const glFont *ft_font, const char *text)
Gets the width that it would take to print some text.
Definition: font.c:960
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:505
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:720
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.
Definition: opengl_render.c:90
static const double c[]
Definition: rng.c:264
On Screen Display element.
Definition: gui_osd.c:21
char *** items
Definition: gui_osd.c:30
unsigned int active
Definition: gui_osd.c:32
char * title
Definition: gui_osd.c:26
int priority
Definition: gui_osd.c:23
char ** msg
Definition: gui_osd.c:29
int duplicates
Definition: gui_osd.c:25
char ** titlew
Definition: gui_osd.c:27
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
size_t l_end
Definition: font.h:45