Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libmupen64plus/mupen64plus-core/src/osd/osd.cpp
2 views
1
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2
* Mupen64plus - osd.cpp *
3
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
4
* Copyright (C) 2008 Nmn Ebenblues *
5
* *
6
* This program is free software; you can redistribute it and/or modify *
7
* it under the terms of the GNU General Public License as published by *
8
* the Free Software Foundation; either version 2 of the License, or *
9
* (at your option) any later version. *
10
* *
11
* This program is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU General Public License for more details. *
15
* *
16
* You should have received a copy of the GNU General Public License *
17
* along with this program; if not, write to the *
18
* Free Software Foundation, Inc., *
19
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
20
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
21
22
// On-screen Display
23
#include <SDL_opengl.h>
24
#include <SDL_thread.h>
25
26
#include "OGLFT.h"
27
#include "osd.h"
28
29
extern "C" {
30
#define M64P_CORE_PROTOTYPES 1
31
#include "api/m64p_config.h"
32
#include "api/config.h"
33
#include "api/callbacks.h"
34
#include "api/m64p_vidext.h"
35
#include "api/vidext.h"
36
#include "main/main.h"
37
#include "main/list.h"
38
#include "osal/files.h"
39
#include "osal/preproc.h"
40
#include "plugin/plugin.h"
41
}
42
43
#define FONT_FILENAME "font.ttf"
44
45
typedef void (APIENTRYP PTRGLACTIVETEXTURE)(GLenum texture);
46
static PTRGLACTIVETEXTURE pglActiveTexture = NULL;
47
48
// static variables for OSD
49
static int l_OsdInitialized = 0;
50
51
static LIST_HEAD(l_messageQueue);
52
static OGLFT::Monochrome *l_font;
53
static float l_fLineHeight = -1.0;
54
55
static void animation_none(osd_message_t *);
56
static void animation_fade(osd_message_t *);
57
static void osd_remove_message(osd_message_t *msg);
58
static osd_message_t * osd_message_valid(osd_message_t *testmsg);
59
60
static float fCornerScroll[OSD_NUM_CORNERS];
61
62
static SDL_mutex *osd_list_lock;
63
64
// animation handlers
65
static void (*l_animations[OSD_NUM_ANIM_TYPES])(osd_message_t *) = {
66
animation_none, // animation handler for OSD_NONE
67
animation_fade // animation handler for OSD_FADE
68
};
69
70
// private functions
71
// draw message on screen
72
static void draw_message(osd_message_t *msg, int width, int height)
73
{
74
float x = 0.,
75
y = 0.;
76
77
if(!l_font || !l_font->isValid())
78
return;
79
80
// set color. alpha is hard coded to 1. animation can change this
81
l_font->setForegroundColor(msg->color[R], msg->color[G], msg->color[B], 1.0);
82
l_font->setBackgroundColor(0.0, 0.0, 0.0, 0.0);
83
84
// set justification based on corner
85
switch(msg->corner)
86
{
87
case OSD_TOP_LEFT:
88
l_font->setVerticalJustification(OGLFT::Face::TOP);
89
l_font->setHorizontalJustification(OGLFT::Face::LEFT);
90
x = 0.;
91
y = (float)height;
92
break;
93
case OSD_TOP_CENTER:
94
l_font->setVerticalJustification(OGLFT::Face::TOP);
95
l_font->setHorizontalJustification(OGLFT::Face::CENTER);
96
x = ((float)width)/2.0f;
97
y = (float)height;
98
break;
99
case OSD_TOP_RIGHT:
100
l_font->setVerticalJustification(OGLFT::Face::TOP);
101
l_font->setHorizontalJustification(OGLFT::Face::RIGHT);
102
x = (float)width;
103
y = (float)height;
104
break;
105
case OSD_MIDDLE_LEFT:
106
l_font->setVerticalJustification(OGLFT::Face::MIDDLE);
107
l_font->setHorizontalJustification(OGLFT::Face::LEFT);
108
x = 0.;
109
y = ((float)height)/2.0f;
110
break;
111
case OSD_MIDDLE_CENTER:
112
l_font->setVerticalJustification(OGLFT::Face::MIDDLE);
113
l_font->setHorizontalJustification(OGLFT::Face::CENTER);
114
x = ((float)width)/2.0f;
115
y = ((float)height)/2.0f;
116
break;
117
case OSD_MIDDLE_RIGHT:
118
l_font->setVerticalJustification(OGLFT::Face::MIDDLE);
119
l_font->setHorizontalJustification(OGLFT::Face::RIGHT);
120
x = (float)width;
121
y = ((float)height)/2.0f;
122
break;
123
case OSD_BOTTOM_LEFT:
124
l_font->setVerticalJustification(OGLFT::Face::BOTTOM);
125
l_font->setHorizontalJustification(OGLFT::Face::LEFT);
126
x = 0.;
127
y = 0.;
128
break;
129
case OSD_BOTTOM_CENTER:
130
l_font->setVerticalJustification(OGLFT::Face::BOTTOM);
131
l_font->setHorizontalJustification(OGLFT::Face::CENTER);
132
x = ((float)width)/2.0f;
133
y = 0.;
134
break;
135
case OSD_BOTTOM_RIGHT:
136
l_font->setVerticalJustification(OGLFT::Face::BOTTOM);
137
l_font->setHorizontalJustification(OGLFT::Face::RIGHT);
138
x = (float)width;
139
y = 0.;
140
break;
141
default:
142
l_font->setVerticalJustification(OGLFT::Face::BOTTOM);
143
l_font->setHorizontalJustification(OGLFT::Face::LEFT);
144
x = 0.;
145
y = 0.;
146
break;
147
}
148
149
// apply animation for current message state
150
(*l_animations[msg->animation[msg->state]])(msg);
151
152
// xoffset moves message left
153
x -= msg->xoffset;
154
// yoffset moves message up
155
y += msg->yoffset;
156
157
// get the bounding box if invalid
158
if (msg->sizebox[0] == 0 && msg->sizebox[2] == 0) // xmin and xmax
159
{
160
OGLFT::BBox bbox = l_font->measure_nominal(msg->text);
161
msg->sizebox[0] = bbox.x_min_;
162
msg->sizebox[1] = bbox.y_min_;
163
msg->sizebox[2] = bbox.x_max_;
164
msg->sizebox[3] = bbox.y_max_;
165
}
166
167
// draw the text line
168
l_font->draw(x, y, msg->text, msg->sizebox);
169
}
170
171
// null animation handler
172
static void animation_none(osd_message_t *msg) { }
173
174
// fade in/out animation handler
175
static void animation_fade(osd_message_t *msg)
176
{
177
float alpha = 1.;
178
float elapsed_frames;
179
float total_frames = (float)msg->timeout[msg->state];
180
181
switch(msg->state)
182
{
183
case OSD_DISAPPEAR:
184
elapsed_frames = (float)(total_frames - msg->frames);
185
break;
186
case OSD_APPEAR:
187
default:
188
elapsed_frames = (float)msg->frames;
189
break;
190
}
191
192
if(total_frames != 0.)
193
alpha = elapsed_frames / total_frames;
194
195
l_font->setForegroundColor(msg->color[R], msg->color[G], msg->color[B], alpha);
196
}
197
198
// sets message Y offset depending on where they are in the message queue
199
static float get_message_offset(osd_message_t *msg, float fLinePos)
200
{
201
float offset = (float) (l_font->height() * fLinePos);
202
203
switch(msg->corner)
204
{
205
case OSD_TOP_LEFT:
206
case OSD_TOP_CENTER:
207
case OSD_TOP_RIGHT:
208
return -offset;
209
break;
210
default:
211
return offset;
212
break;
213
}
214
}
215
216
// public functions
217
extern "C"
218
void osd_init(int width, int height)
219
{
220
const char *fontpath;
221
222
osd_list_lock = SDL_CreateMutex();
223
if (!osd_list_lock) {
224
DebugMessage(M64MSG_ERROR, "Could not create osd list lock");
225
return;
226
}
227
228
if (!OGLFT::Init_FT())
229
{
230
DebugMessage(M64MSG_ERROR, "Could not initialize freetype library.");
231
return;
232
}
233
234
fontpath = ConfigGetSharedDataFilepath(FONT_FILENAME);
235
236
l_font = new OGLFT::Monochrome(fontpath, (float) height / 35.0f); // make font size proportional to screen height
237
238
if(!l_font || !l_font->isValid())
239
{
240
DebugMessage(M64MSG_ERROR, "Could not construct face from %s", fontpath);
241
return;
242
}
243
244
// clear statics
245
for (int i = 0; i < OSD_NUM_CORNERS; i++)
246
fCornerScroll[i] = 0.0;
247
248
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
249
#if defined(GL_RASTER_POSITION_UNCLIPPED_IBM)
250
glEnable(GL_RASTER_POSITION_UNCLIPPED_IBM);
251
#endif
252
253
pglActiveTexture = (PTRGLACTIVETEXTURE) VidExt_GL_GetProcAddress("glActiveTexture");
254
if (pglActiveTexture == NULL)
255
{
256
DebugMessage(M64MSG_WARNING, "OpenGL function glActiveTexture() not supported. OSD deactivated.");
257
return;
258
}
259
260
// set initialized flag
261
l_OsdInitialized = 1;
262
}
263
264
extern "C"
265
void osd_exit(void)
266
{
267
osd_message_t *msg, *safe;
268
269
// delete font renderer
270
if (l_font)
271
{
272
delete l_font;
273
l_font = NULL;
274
}
275
276
// delete message queue
277
SDL_LockMutex(osd_list_lock);
278
list_for_each_entry_safe(msg, safe, &l_messageQueue, osd_message_t, list) {
279
osd_remove_message(msg);
280
if (!msg->user_managed)
281
free(msg);
282
}
283
SDL_UnlockMutex(osd_list_lock);
284
285
// shut down the Freetype library
286
OGLFT::Uninit_FT();
287
288
SDL_DestroyMutex(osd_list_lock);
289
290
// reset initialized flag
291
l_OsdInitialized = 0;
292
}
293
294
// renders the current osd message queue to the screen
295
extern "C"
296
void osd_render()
297
{
298
osd_message_t *msg, *safe;
299
int i;
300
301
// if we're not initialized or list is empty, then just skip it all
302
if (!l_OsdInitialized || list_empty(&l_messageQueue))
303
return;
304
305
// get the viewport dimensions
306
GLint viewport[4];
307
glGetIntegerv(GL_VIEWPORT, viewport);
308
309
// save all the attributes
310
glPushAttrib(GL_ALL_ATTRIB_BITS);
311
bool bFragmentProg = glIsEnabled(GL_FRAGMENT_PROGRAM_ARB) != 0;
312
bool bColorArray = glIsEnabled(GL_COLOR_ARRAY) != 0;
313
bool bTexCoordArray = glIsEnabled(GL_TEXTURE_COORD_ARRAY) != 0;
314
bool bSecColorArray = glIsEnabled(GL_SECONDARY_COLOR_ARRAY) != 0;
315
316
// deactivate all the texturing units
317
GLint iActiveTex;
318
bool bTexture2D[8];
319
glGetIntegerv(GL_ACTIVE_TEXTURE_ARB, &iActiveTex);
320
for (i = 0; i < 8; i++)
321
{
322
pglActiveTexture(GL_TEXTURE0_ARB + i);
323
bTexture2D[i] = glIsEnabled(GL_TEXTURE_2D) != 0;
324
glDisable(GL_TEXTURE_2D);
325
}
326
327
// save the matrices and set up new ones
328
glMatrixMode(GL_PROJECTION);
329
glPushMatrix();
330
glLoadIdentity();
331
gluOrtho2D(viewport[0],viewport[2],viewport[1],viewport[3]);
332
333
glMatrixMode(GL_MODELVIEW);
334
glPushMatrix();
335
glLoadIdentity();
336
337
// setup for drawing text
338
glDisable(GL_FOG);
339
glDisable(GL_LIGHTING);
340
glDisable(GL_ALPHA_TEST);
341
glDisable(GL_DEPTH_TEST);
342
glDisable(GL_CULL_FACE);
343
glDisable(GL_SCISSOR_TEST);
344
glDisable(GL_STENCIL_TEST);
345
glDisable(GL_FRAGMENT_PROGRAM_ARB);
346
glDisable(GL_COLOR_MATERIAL);
347
348
glEnable(GL_BLEND);
349
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
350
351
glDisableClientState(GL_COLOR_ARRAY);
352
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
353
glDisableClientState(GL_SECONDARY_COLOR_ARRAY);
354
glShadeModel(GL_FLAT);
355
356
// get line height if invalid
357
if (l_fLineHeight < 0.0)
358
{
359
OGLFT::BBox bbox = l_font->measure("01abjZpqRGB");
360
l_fLineHeight = (bbox.y_max_ - bbox.y_min_) / 30.0f;
361
}
362
363
// keeps track of next message position for each corner
364
float fCornerPos[OSD_NUM_CORNERS];
365
for (i = 0; i < OSD_NUM_CORNERS; i++)
366
fCornerPos[i] = 0.5f * l_fLineHeight;
367
368
SDL_LockMutex(osd_list_lock);
369
list_for_each_entry_safe(msg, safe, &l_messageQueue, osd_message_t, list) {
370
// update message state
371
if(msg->timeout[msg->state] != OSD_INFINITE_TIMEOUT &&
372
++msg->frames >= msg->timeout[msg->state])
373
{
374
// if message is in last state, mark it for deletion and continue to the next message
375
if(msg->state >= OSD_NUM_STATES - 1)
376
{
377
if (msg->user_managed) {
378
osd_remove_message(msg);
379
} else {
380
osd_remove_message(msg);
381
free(msg);
382
}
383
384
continue;
385
}
386
387
// go to next state and reset frame count
388
msg->state++;
389
msg->frames = 0;
390
}
391
392
// offset y depending on how many other messages are in the same corner
393
float fStartOffset;
394
if (msg->corner >= OSD_MIDDLE_LEFT && msg->corner <= OSD_MIDDLE_RIGHT) // don't scroll the middle messages
395
fStartOffset = fCornerPos[msg->corner];
396
else
397
fStartOffset = fCornerPos[msg->corner] + (fCornerScroll[msg->corner] * l_fLineHeight);
398
msg->yoffset += get_message_offset(msg, fStartOffset);
399
400
draw_message(msg, viewport[2], viewport[3]);
401
402
msg->yoffset -= get_message_offset(msg, fStartOffset);
403
fCornerPos[msg->corner] += l_fLineHeight;
404
}
405
SDL_UnlockMutex(osd_list_lock);
406
407
// do the scrolling
408
for (int i = 0; i < OSD_NUM_CORNERS; i++)
409
{
410
fCornerScroll[i] += 0.1f;
411
if (fCornerScroll[i] >= 0.0)
412
fCornerScroll[i] = 0.0;
413
}
414
415
// restore the matrices
416
glMatrixMode(GL_MODELVIEW);
417
glPopMatrix();
418
glMatrixMode(GL_PROJECTION);
419
glPopMatrix();
420
421
// restore the attributes
422
for (int i = 0; i < 8; i++)
423
{
424
pglActiveTexture(GL_TEXTURE0_ARB + i);
425
if (bTexture2D[i])
426
glEnable(GL_TEXTURE_2D);
427
else
428
glDisable(GL_TEXTURE_2D);
429
}
430
pglActiveTexture(iActiveTex);
431
glPopAttrib();
432
if (bFragmentProg)
433
glEnable(GL_FRAGMENT_PROGRAM_ARB);
434
if (bColorArray)
435
glEnableClientState(GL_COLOR_ARRAY);
436
if (bTexCoordArray)
437
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
438
if (bSecColorArray)
439
glEnableClientState(GL_SECONDARY_COLOR_ARRAY);
440
441
glFinish();
442
}
443
444
// creates a new osd_message_t, adds it to the message queue and returns it in case
445
// the user wants to modify its parameters. Note, if the message can't be created,
446
// NULL is returned.
447
extern "C"
448
osd_message_t * osd_new_message(enum osd_corner eCorner, const char *fmt, ...)
449
{
450
va_list ap;
451
char buf[1024];
452
453
if (!l_OsdInitialized) return NULL;
454
455
osd_message_t *msg = (osd_message_t *)malloc(sizeof(*msg));
456
457
if (!msg) return NULL;
458
459
va_start(ap, fmt);
460
vsnprintf(buf, 1024, fmt, ap);
461
buf[1023] = 0;
462
va_end(ap);
463
464
// set default values
465
memset(msg, 0, sizeof(osd_message_t));
466
msg->text = strdup(buf);
467
msg->user_managed = 0;
468
// default to white
469
msg->color[R] = 1.;
470
msg->color[G] = 1.;
471
msg->color[B] = 1.;
472
473
msg->sizebox[0] = 0.0; // set a null bounding box
474
msg->sizebox[1] = 0.0;
475
msg->sizebox[2] = 0.0;
476
msg->sizebox[3] = 0.0;
477
478
msg->corner = eCorner;
479
msg->state = OSD_APPEAR;
480
fCornerScroll[eCorner] -= 1.0; // start this one before the beginning of the list and scroll it in
481
482
msg->animation[OSD_APPEAR] = OSD_FADE;
483
msg->animation[OSD_DISPLAY] = OSD_NONE;
484
msg->animation[OSD_DISAPPEAR] = OSD_FADE;
485
486
if (eCorner >= OSD_MIDDLE_LEFT && eCorner <= OSD_MIDDLE_RIGHT)
487
{
488
msg->timeout[OSD_APPEAR] = 20;
489
msg->timeout[OSD_DISPLAY] = 60;
490
msg->timeout[OSD_DISAPPEAR] = 20;
491
}
492
else
493
{
494
msg->timeout[OSD_APPEAR] = 20;
495
msg->timeout[OSD_DISPLAY] = 180;
496
msg->timeout[OSD_DISAPPEAR] = 40;
497
}
498
499
// add to message queue
500
SDL_LockMutex(osd_list_lock);
501
list_add(&msg->list, &l_messageQueue);
502
SDL_UnlockMutex(osd_list_lock);
503
504
return msg;
505
}
506
507
// update message string
508
extern "C"
509
void osd_update_message(osd_message_t *msg, const char *fmt, ...)
510
{
511
va_list ap;
512
char buf[1024];
513
514
if (!l_OsdInitialized || !msg) return;
515
516
va_start(ap, fmt);
517
vsnprintf(buf, 1024, fmt, ap);
518
buf[1023] = 0;
519
va_end(ap);
520
521
free(msg->text);
522
msg->text = strdup(buf);
523
524
// reset bounding box
525
msg->sizebox[0] = 0.0;
526
msg->sizebox[1] = 0.0;
527
msg->sizebox[2] = 0.0;
528
msg->sizebox[3] = 0.0;
529
530
// reset display time counter
531
if (msg->state >= OSD_DISPLAY)
532
{
533
msg->state = OSD_DISPLAY;
534
msg->frames = 0;
535
}
536
537
SDL_LockMutex(osd_list_lock);
538
if (!osd_message_valid(msg))
539
list_add(&msg->list, &l_messageQueue);
540
SDL_UnlockMutex(osd_list_lock);
541
542
}
543
544
// remove message from message queue
545
static void osd_remove_message(osd_message_t *msg)
546
{
547
if (!l_OsdInitialized || !msg) return;
548
549
free(msg->text);
550
msg->text = NULL;
551
list_del(&msg->list);
552
}
553
554
// remove message from message queue and free it
555
extern "C"
556
void osd_delete_message(osd_message_t *msg)
557
{
558
if (!l_OsdInitialized || !msg) return;
559
560
SDL_LockMutex(osd_list_lock);
561
osd_remove_message(msg);
562
free(msg);
563
SDL_UnlockMutex(osd_list_lock);
564
}
565
566
// set message so it doesn't automatically expire in a certain number of frames.
567
extern "C"
568
void osd_message_set_static(osd_message_t *msg)
569
{
570
if (!l_OsdInitialized || !msg) return;
571
572
msg->timeout[OSD_DISPLAY] = OSD_INFINITE_TIMEOUT;
573
msg->state = OSD_DISPLAY;
574
msg->frames = 0;
575
}
576
577
// set message so it doesn't automatically get freed when finished transition.
578
extern "C"
579
void osd_message_set_user_managed(osd_message_t *msg)
580
{
581
if (!l_OsdInitialized || !msg) return;
582
583
msg->user_managed = 1;
584
}
585
586
// return message pointer if valid (in the OSD list), otherwise return NULL
587
static osd_message_t * osd_message_valid(osd_message_t *testmsg)
588
{
589
osd_message_t *msg;
590
591
if (!l_OsdInitialized || !testmsg) return NULL;
592
593
list_for_each_entry(msg, &l_messageQueue, osd_message_t, list) {
594
if (msg == testmsg)
595
return testmsg;
596
}
597
598
return NULL;
599
}
600
601
602