1
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2
* Mupen64plus - osd.cpp *
3
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
4
* Copyright (C) 2008 Nmn Ebenblues *
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. *
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. *
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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
29
#include "../main/main.h"
30
#include "../main/plugin.h"
31
#include "../main/util.h"
34
#define FONT_FILENAME "font.ttf"
37
# warning This hack should be fixed at some point
38
# define glActiveTexture(x)
41
// static variables for OSD
42
static int l_OsdInitialized = 0;
44
static list_t l_messageQueue = NULL;
45
static OGLFT::Monochrome *l_font;
46
static float l_fLineHeight = -1.0;
48
static void animation_none(osd_message_t *);
49
static void animation_fade(osd_message_t *);
51
static float fCornerScroll[OSD_NUM_CORNERS];
54
static void (*l_animations[OSD_NUM_ANIM_TYPES])(osd_message_t *) = {
55
animation_none, // animation handler for OSD_NONE
56
animation_fade // animation handler for OSD_FADE
60
// draw message on screen
61
static void draw_message(osd_message_t *msg, int width, int height)
66
if(!l_font || !l_font->isValid())
69
// set color. alpha is hard coded to 1. animation can change this
70
l_font->setForegroundColor(msg->color[R], msg->color[G], msg->color[B], 1.0);
71
l_font->setBackgroundColor(0.0, 0.0, 0.0, 0.0);
73
// set justification based on corner
77
l_font->setVerticalJustification(OGLFT::Face::TOP);
78
l_font->setHorizontalJustification(OGLFT::Face::LEFT);
83
l_font->setVerticalJustification(OGLFT::Face::TOP);
84
l_font->setHorizontalJustification(OGLFT::Face::CENTER);
85
x = ((float)width)/2.0;
89
l_font->setVerticalJustification(OGLFT::Face::TOP);
90
l_font->setHorizontalJustification(OGLFT::Face::RIGHT);
95
l_font->setVerticalJustification(OGLFT::Face::MIDDLE);
96
l_font->setHorizontalJustification(OGLFT::Face::LEFT);
98
y = ((float)height)/2.0;
100
case OSD_MIDDLE_CENTER:
101
l_font->setVerticalJustification(OGLFT::Face::MIDDLE);
102
l_font->setHorizontalJustification(OGLFT::Face::CENTER);
103
x = ((float)width)/2.0;
104
y = ((float)height)/2.0;
106
case OSD_MIDDLE_RIGHT:
107
l_font->setVerticalJustification(OGLFT::Face::MIDDLE);
108
l_font->setHorizontalJustification(OGLFT::Face::RIGHT);
110
y = ((float)height)/2.0;
112
case OSD_BOTTOM_LEFT:
113
l_font->setVerticalJustification(OGLFT::Face::BOTTOM);
114
l_font->setHorizontalJustification(OGLFT::Face::LEFT);
118
case OSD_BOTTOM_CENTER:
119
l_font->setVerticalJustification(OGLFT::Face::BOTTOM);
120
l_font->setHorizontalJustification(OGLFT::Face::CENTER);
121
x = ((float)width)/2.0;
124
case OSD_BOTTOM_RIGHT:
125
l_font->setVerticalJustification(OGLFT::Face::BOTTOM);
126
l_font->setHorizontalJustification(OGLFT::Face::RIGHT);
131
l_font->setVerticalJustification(OGLFT::Face::BOTTOM);
132
l_font->setHorizontalJustification(OGLFT::Face::LEFT);
138
// apply animation for current message state
139
(*l_animations[msg->animation[msg->state]])(msg);
141
// xoffset moves message left
143
// yoffset moves message up
146
// get the bounding box if invalid
147
if (msg->sizebox[0] == 0 && msg->sizebox[2] == 0) // xmin and xmax
149
OGLFT::BBox bbox = l_font->measure_nominal(msg->text);
150
msg->sizebox[0] = bbox.x_min_;
151
msg->sizebox[1] = bbox.y_min_;
152
msg->sizebox[2] = bbox.x_max_;
153
msg->sizebox[3] = bbox.y_max_;
156
// draw the text line
157
l_font->draw(x, y, msg->text, msg->sizebox);
160
// null animation handler
161
static void animation_none(osd_message_t *msg) { }
163
// fade in/out animation handler
164
static void animation_fade(osd_message_t *msg)
167
float elapsed_frames;
168
float total_frames = (float)msg->timeout[msg->state];
173
elapsed_frames = (float)(total_frames - msg->frames);
177
elapsed_frames = (float)msg->frames;
181
if(total_frames != 0.)
182
alpha = elapsed_frames / total_frames;
184
l_font->setForegroundColor(msg->color[R], msg->color[G], msg->color[B], alpha);
187
// sets message Y offset depending on where they are in the message queue
188
static float get_message_offset(osd_message_t *msg, float fLinePos)
190
float offset = l_font->height() * fLinePos;
207
void osd_init(int width, int height)
209
char fontpath[PATH_MAX];
211
snprintf(fontpath, PATH_MAX, "%sfonts/%s", get_installpath(), FONT_FILENAME);
212
l_font = new OGLFT::Monochrome(fontpath, height / 35); // make font size proportional to screen height
214
if(!l_font || !l_font->isValid())
216
printf("Could not construct face from %s\n", fontpath);
221
for (int i = 0; i < OSD_NUM_CORNERS; i++)
222
fCornerScroll[i] = 0.0;
224
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
225
#if defined(GL_RASTER_POSITION_UNCLIPPED_IBM)
226
glEnable(GL_RASTER_POSITION_UNCLIPPED_IBM);
229
// set initialized flag
230
l_OsdInitialized = 1;
239
// delete font renderer
246
// delete message queue
247
list_foreach(l_messageQueue, node)
249
msg = (osd_message_t *)node->data;
255
list_delete(&l_messageQueue);
257
// reset initialized flag
258
l_OsdInitialized = 0;
261
// renders the current osd message queue to the screen
266
osd_message_t *msg, *msg_to_delete = NULL;
269
// if we're not initialized or list is empty, then just skip it all
270
if (!l_OsdInitialized || l_messageQueue == NULL)
273
// get the viewport dimensions
275
glGetIntegerv(GL_VIEWPORT, viewport);
277
// save all the attributes
278
glPushAttrib(GL_ALL_ATTRIB_BITS);
279
bool bFragmentProg = glIsEnabled(GL_FRAGMENT_PROGRAM_ARB);
280
bool bColorArray = glIsEnabled(GL_COLOR_ARRAY);
281
bool bTexCoordArray = glIsEnabled(GL_TEXTURE_COORD_ARRAY);
282
bool bSecColorArray = glIsEnabled(GL_SECONDARY_COLOR_ARRAY);
284
// deactivate all the texturing units
287
glGetIntegerv(GL_ACTIVE_TEXTURE_ARB, &iActiveTex);
288
for (i = 0; i < 8; i++)
290
glActiveTexture(GL_TEXTURE0_ARB + i);
291
bTexture2D[i] = glIsEnabled(GL_TEXTURE_2D);
292
glDisable(GL_TEXTURE_2D);
295
// save the matrices and set up new ones
296
glMatrixMode(GL_PROJECTION);
299
gluOrtho2D(viewport[0],viewport[2],viewport[1],viewport[3]);
301
glMatrixMode(GL_MODELVIEW);
305
// setup for drawing text
307
glDisable(GL_LIGHTING);
308
glDisable(GL_DEPTH_TEST);
309
glDisable(GL_CULL_FACE);
310
glDisable(GL_SCISSOR_TEST);
311
glDisable(GL_STENCIL_TEST);
312
glDisable(GL_FRAGMENT_PROGRAM_ARB);
313
glDisable(GL_REGISTER_COMBINERS_NV);
314
glDisable(GL_COLOR_MATERIAL);
317
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
319
glDisableClientState(GL_COLOR_ARRAY);
320
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
321
glDisableClientState(GL_SECONDARY_COLOR_ARRAY);
322
glShadeModel(GL_FLAT);
324
// get line height if invalid
325
if (l_fLineHeight < 0.0)
327
OGLFT::BBox bbox = l_font->measure("01abjZpqRGB");
328
l_fLineHeight = (bbox.y_max_ - bbox.y_min_) / 30.0;
331
// keeps track of next message position for each corner
332
float fCornerPos[OSD_NUM_CORNERS];
333
for (i = 0; i < OSD_NUM_CORNERS; i++)
334
fCornerPos[i] = 0.5 * l_fLineHeight;
336
list_foreach(l_messageQueue, node)
338
msg = (osd_message_t *)node->data;
340
// if previous message was marked for deletion, delete it
343
osd_delete_message(msg_to_delete);
344
msg_to_delete = NULL;
347
// update message state
348
if(msg->timeout[msg->state] != OSD_INFINITE_TIMEOUT &&
349
++msg->frames >= msg->timeout[msg->state])
351
// if message is in last state, mark it for deletion and continue to the next message
352
if(msg->state >= OSD_NUM_STATES - 1)
358
// go to next state and reset frame count
363
// offset y depending on how many other messages are in the same corner
365
if (msg->corner >= OSD_MIDDLE_LEFT && msg->corner <= OSD_MIDDLE_RIGHT) // don't scroll the middle messages
366
fStartOffset = fCornerPos[msg->corner];
368
fStartOffset = fCornerPos[msg->corner] + (fCornerScroll[msg->corner] * l_fLineHeight);
369
msg->yoffset += get_message_offset(msg, fStartOffset);
371
draw_message(msg, viewport[2], viewport[3]);
373
msg->yoffset -= get_message_offset(msg, fStartOffset);
374
fCornerPos[msg->corner] += l_fLineHeight;
378
for (int i = 0; i < OSD_NUM_CORNERS; i++)
380
fCornerScroll[i] += 0.1;
381
if (fCornerScroll[i] >= 0.0)
382
fCornerScroll[i] = 0.0;
385
// if last message was marked for deletion, delete it
387
osd_delete_message(msg_to_delete);
389
// restore the matrices
390
glMatrixMode(GL_MODELVIEW);
392
glMatrixMode(GL_PROJECTION);
395
// restore the attributes
396
for (int i = 0; i < 8; i++)
398
glActiveTexture(GL_TEXTURE0_ARB + i);
400
glEnable(GL_TEXTURE_2D);
402
glDisable(GL_TEXTURE_2D);
404
glActiveTexture(iActiveTex);
407
glEnable(GL_FRAGMENT_PROGRAM_ARB);
409
glEnableClientState(GL_COLOR_ARRAY);
411
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
413
glEnableClientState(GL_SECONDARY_COLOR_ARRAY);
418
// creates a new osd_message_t, adds it to the message queue and returns it in case
419
// the user wants to modify its parameters. Note, if the message can't be created,
422
osd_message_t * osd_new_message(enum osd_corner eCorner, const char *fmt, ...)
427
if (!l_OsdInitialized) return NULL;
429
osd_message_t *msg = (osd_message_t *)malloc(sizeof(osd_message_t));
431
if (!msg) return NULL;
434
vsnprintf(buf, PATH_MAX, fmt, ap);
437
// set default values
438
memset(msg, 0, sizeof(osd_message_t));
439
msg->text = strdup(buf);
445
msg->sizebox[0] = 0.0; // set a null bounding box
446
msg->sizebox[1] = 0.0;
447
msg->sizebox[2] = 0.0;
448
msg->sizebox[3] = 0.0;
450
msg->corner = eCorner;
451
msg->state = OSD_APPEAR;
452
fCornerScroll[eCorner] -= 1.0; // start this one before the beginning of the list and scroll it in
454
msg->animation[OSD_APPEAR] = OSD_FADE;
455
msg->animation[OSD_DISPLAY] = OSD_NONE;
456
msg->animation[OSD_DISAPPEAR] = OSD_FADE;
458
if (eCorner >= OSD_MIDDLE_LEFT && eCorner <= OSD_MIDDLE_RIGHT)
460
msg->timeout[OSD_APPEAR] = 20;
461
msg->timeout[OSD_DISPLAY] = 60;
462
msg->timeout[OSD_DISAPPEAR] = 20;
466
msg->timeout[OSD_APPEAR] = 20;
467
msg->timeout[OSD_DISPLAY] = 180;
468
msg->timeout[OSD_DISAPPEAR] = 40;
471
// add to message queue
472
list_prepend(&l_messageQueue, msg);
477
// update message string
479
void osd_update_message(osd_message_t *msg, const char *fmt, ...)
484
if (!l_OsdInitialized || !msg) return;
487
vsnprintf(buf, PATH_MAX, fmt, ap);
493
msg->text = strdup(buf);
495
// reset bounding box
496
msg->sizebox[0] = 0.0;
497
msg->sizebox[1] = 0.0;
498
msg->sizebox[2] = 0.0;
499
msg->sizebox[3] = 0.0;
501
// reset display time counter
502
if (msg->state >= OSD_DISPLAY)
504
msg->state = OSD_DISPLAY;
510
// remove message from message queue
512
void osd_delete_message(osd_message_t *msg)
516
if (!l_OsdInitialized || !msg) return;
521
node = list_find_node(l_messageQueue, msg);
523
list_node_delete(&l_messageQueue, node);
526
// set "corner" of the screen that message appears in.
528
void osd_message_set_corner(osd_message_t *msg, enum osd_corner corner)
530
if (!l_OsdInitialized || !msg) return;
532
msg->corner = corner;
535
// set message so it doesn't automatically expire in a certain number of frames.
537
void osd_message_set_static(osd_message_t *msg)
539
if (!l_OsdInitialized || !msg) return;
541
msg->timeout[OSD_DISPLAY] = OSD_INFINITE_TIMEOUT;
542
msg->state = OSD_DISPLAY;
546
// return message pointer if valid (in the OSD list), otherwise return NULL
548
osd_message_t * osd_message_valid(osd_message_t *testmsg)
550
if (!l_OsdInitialized || !testmsg) return NULL;
552
if (list_find_node(l_messageQueue, testmsg) == NULL)