1 /*
2  * Copyright (C) 2000-2019 the xine project
3  *
4  * This file is part of xine, a unix video player.
5  *
6  * xine 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  * xine 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 Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  *
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <unistd.h>
31 #include <pthread.h>
32 
33 #include "common.h"
34 
35 
36 #define OVL_PALETTE_SIZE 256
37 
38 #ifdef	__GNUC__
39 #define CLUT_Y_CR_CB_INIT(_y,_cr,_cb)	{{ .y = (_y), .cr = (_cr), .cb = (_cb)}}
40 #else
41 #define CLUT_Y_CR_CB_INIT(_y,_cr,_cb)	{{ (_cb), (_cr), (_y) }}
42 #endif
43 
44 static const union {         /* CLUT == Color LookUp Table */
45   struct {
46     uint8_t cb    : 8;
47     uint8_t cr    : 8;
48     uint8_t y     : 8;
49     uint8_t foo   : 8;
50   } u8;
51   uint32_t u32;
52 } __attribute__ ((packed)) textpalettes_color[OVL_PALETTE_SIZE] = {
53   /* white, no border, translucid */
54     CLUT_Y_CR_CB_INIT(0x00, 0x00, 0x00), //0
55     CLUT_Y_CR_CB_INIT(0x60, 0x80, 0x80), //1
56     CLUT_Y_CR_CB_INIT(0x70, 0x80, 0x80), //2
57     CLUT_Y_CR_CB_INIT(0x80, 0x80, 0x80), //3
58     CLUT_Y_CR_CB_INIT(0x80, 0x80, 0x80), //4
59     CLUT_Y_CR_CB_INIT(0x80, 0x80, 0x80), //5
60     CLUT_Y_CR_CB_INIT(0x80, 0x80, 0x80), //6
61     CLUT_Y_CR_CB_INIT(0xa0, 0x80, 0x80), //7
62     CLUT_Y_CR_CB_INIT(0xc0, 0x80, 0x80), //8
63     CLUT_Y_CR_CB_INIT(0xe0, 0x80, 0x80), //9
64     CLUT_Y_CR_CB_INIT(0xff, 0x80, 0x80), //10
65   /* yellow, black border, transparent */
66     CLUT_Y_CR_CB_INIT(0x00, 0x00, 0x00), //0
67     CLUT_Y_CR_CB_INIT(0x80, 0x80, 0xe0), //1
68     CLUT_Y_CR_CB_INIT(0x80, 0x80, 0xc0), //2
69     CLUT_Y_CR_CB_INIT(0x60, 0x80, 0xa0), //3
70     CLUT_Y_CR_CB_INIT(0x40, 0x80, 0x80), //4
71     CLUT_Y_CR_CB_INIT(0x20, 0x80, 0x80), //5
72     CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), //6
73     CLUT_Y_CR_CB_INIT(0x40, 0x84, 0x60), //7
74     CLUT_Y_CR_CB_INIT(0xd0, 0x88, 0x40), //8
75     CLUT_Y_CR_CB_INIT(0xe0, 0x8a, 0x00), //9
76     CLUT_Y_CR_CB_INIT(0xff, 0x90, 0x00), //10
77 };
78 
79 static const uint8_t textpalettes_trans[OVL_PALETTE_SIZE] = {
80   /* white, no border, translucid */
81   0, 8, 9, 10, 11, 12, 13, 14, 15, 15, 15,
82   /* yellow, black border, transparent */
83   0, 8, 9, 10, 11, 12, 13, 14, 15, 15, 15,
84 };
85 
86 static const struct {
87   char     symbol[4];
88   int      status;
89 } xine_status[] = {
90   { "\xD8",  XINE_STATUS_IDLE  }, /* Ø */
91   { "}",  XINE_STATUS_STOP  },
92   { ">" , XINE_STATUS_PLAY  },
93   { "{" , XINE_STATUS_QUIT  }
94 };
95 
96 static const struct {
97   char     symbol[4];
98   int      speed;
99 } xine_speeds[] = {
100   { "<"  , XINE_SPEED_PAUSE  },
101   { "@@>", XINE_SPEED_SLOW_4 },
102   { "@>" , XINE_SPEED_SLOW_2 },
103   { ">"  , XINE_SPEED_NORMAL },
104   { ">$" , XINE_SPEED_FAST_2 },
105   { ">$$", XINE_SPEED_FAST_4 }
106 };
107 
108 #define BAR_WIDTH 336
109 #define BAR_HEIGHT 25
110 
111 #define MINIMUM_WIN_WIDTH  300
112 #define FONT_SIZE          20
113 #define UNSCALED_FONT_SIZE 24
114 
115 static pthread_mutex_t osd_mutex;
116 
_xine_osd_show(xine_osd_t * osd,int64_t vpts)117 static void  _xine_osd_show(xine_osd_t *osd, int64_t vpts) {
118   if( gGui->osd.use_unscaled && gGui->osd.unscaled_available )
119     xine_osd_show_unscaled(osd, vpts);
120   else
121     xine_osd_show(osd, vpts);
122 }
123 
_osd_get_output_size(int * w,int * h)124 static void _osd_get_output_size(int *w, int *h) {
125   if( gGui->osd.use_unscaled && gGui->osd.unscaled_available )
126     video_window_get_output_size (gGui->vwin, w, h);
127   else
128     video_window_get_frame_size (gGui->vwin, w, h);
129 }
130 
_osd_get_speed_sym(int speed)131 static const char *_osd_get_speed_sym(int speed) {
132   int i;
133 
134   for(i = 0; i < sizeof(xine_speeds)/sizeof(xine_speeds[0]); i++) {
135     if(speed == xine_speeds[i].speed)
136       return xine_speeds[i].symbol;
137   }
138 
139   return NULL;
140 }
_osd_get_status_sym(int status)141 static const char *_osd_get_status_sym(int status) {
142   int i;
143 
144   for(i = 0; i < sizeof(xine_status)/sizeof(xine_status[0]); i++) {
145     if(status == xine_status[i].status)
146       return xine_status[i].symbol;
147   }
148 
149   return NULL;
150 }
151 
osd_init(void)152 void osd_init(void) {
153   int fonth = FONT_SIZE;
154 
155   gGui->osd.sinfo.osd[0] = xine_osd_new(gGui->stream, 0, 0, 900, (fonth * 6) + (5 * 3));
156   xine_osd_set_font(gGui->osd.sinfo.osd[0], "sans", fonth);
157   xine_osd_set_text_palette(gGui->osd.sinfo.osd[0],
158 			    XINE_TEXTPALETTE_WHITE_BLACK_TRANSPARENT, XINE_OSD_TEXT1);
159 
160   gGui->osd.bar.osd[0] = xine_osd_new(gGui->stream, 0, 0, BAR_WIDTH + 1, BAR_HEIGHT + 1);
161 
162   xine_osd_set_palette(gGui->osd.bar.osd[0], &textpalettes_color[0].u32, textpalettes_trans);
163 
164   gGui->osd.bar.osd[1] = xine_osd_new(gGui->stream, 0, 0, BAR_WIDTH + 1, BAR_HEIGHT + 1);
165   xine_osd_set_font(gGui->osd.bar.osd[1], "sans", fonth);
166   xine_osd_set_text_palette(gGui->osd.bar.osd[1],
167 			    XINE_TEXTPALETTE_WHITE_BLACK_TRANSPARENT, XINE_OSD_TEXT1);
168 
169   gGui->osd.status.osd[0] = xine_osd_new(gGui->stream, 0, 0, 300, 2 * fonth);
170   xine_osd_set_text_palette(gGui->osd.status.osd[0],
171 			    XINE_TEXTPALETTE_WHITE_BLACK_TRANSPARENT, XINE_OSD_TEXT1);
172 
173   gGui->osd.info.osd[0] = xine_osd_new(gGui->stream, 0, 0, 2048, fonth + (fonth >> 1));
174   xine_osd_set_font(gGui->osd.info.osd[0], "sans", fonth);
175   xine_osd_set_text_palette(gGui->osd.info.osd[0],
176 			    XINE_TEXTPALETTE_WHITE_BLACK_TRANSPARENT, XINE_OSD_TEXT1);
177 
178   gGui->osd.unscaled_available =
179     (xine_osd_get_capabilities(gGui->osd.status.osd[0]) & XINE_OSD_CAP_UNSCALED );
180 
181   pthread_mutex_init(&osd_mutex, NULL);
182 }
183 
osd_hide_sinfo(void)184 void osd_hide_sinfo(void) {
185 
186   pthread_mutex_lock(&osd_mutex);
187   if(gGui->osd.sinfo.visible) {
188     gGui->osd.sinfo.visible = 0;
189     xine_osd_hide(gGui->osd.sinfo.osd[0], 0);
190   }
191   pthread_mutex_unlock(&osd_mutex);
192 }
193 
osd_hide_bar(void)194 void osd_hide_bar(void) {
195 
196   pthread_mutex_lock(&osd_mutex);
197   if(gGui->osd.bar.visible) {
198     gGui->osd.bar.visible = 0;
199     xine_osd_hide(gGui->osd.bar.osd[0], 0);
200     xine_osd_hide(gGui->osd.bar.osd[1], 0);
201   }
202   pthread_mutex_unlock(&osd_mutex);
203 }
204 
osd_hide_status(void)205 void osd_hide_status(void) {
206 
207   pthread_mutex_lock(&osd_mutex);
208   if(gGui->osd.status.visible) {
209     gGui->osd.status.visible = 0;
210     xine_osd_hide(gGui->osd.status.osd[0], 0);
211   }
212   pthread_mutex_unlock(&osd_mutex);
213 }
214 
osd_hide_info(void)215 void osd_hide_info(void) {
216 
217   pthread_mutex_lock(&osd_mutex);
218   if(gGui->osd.info.visible) {
219     gGui->osd.info.visible = 0;
220     xine_osd_hide(gGui->osd.info.osd[0], 0);
221   }
222   pthread_mutex_unlock(&osd_mutex);
223 }
224 
osd_hide(void)225 void osd_hide(void) {
226 
227   osd_hide_sinfo();
228   osd_hide_bar();
229   osd_hide_status();
230   osd_hide_info();
231 }
232 
osd_deinit(void)233 void osd_deinit(void) {
234 
235   osd_hide();
236 
237   xine_osd_free(gGui->osd.sinfo.osd[0]);
238   xine_osd_free(gGui->osd.bar.osd[0]);
239   xine_osd_free(gGui->osd.bar.osd[1]);
240   xine_osd_free(gGui->osd.status.osd[0]);
241   xine_osd_free(gGui->osd.info.osd[0]);
242 
243   pthread_mutex_destroy(&osd_mutex);
244 }
245 
osd_update(void)246 void osd_update(void) {
247 
248   pthread_mutex_lock(&osd_mutex);
249 
250   if(gGui->osd.sinfo.visible) {
251     gGui->osd.sinfo.visible--;
252     if(!gGui->osd.sinfo.visible) {
253       xine_osd_hide(gGui->osd.sinfo.osd[0], 0);
254     }
255   }
256 
257   if(gGui->osd.bar.visible) {
258     gGui->osd.bar.visible--;
259     if(!gGui->osd.bar.visible) {
260       xine_osd_hide(gGui->osd.bar.osd[0], 0);
261       xine_osd_hide(gGui->osd.bar.osd[1], 0);
262     }
263   }
264 
265   if(gGui->osd.status.visible) {
266     gGui->osd.status.visible--;
267     if(!gGui->osd.status.visible) {
268       xine_osd_hide(gGui->osd.status.osd[0], 0);
269     }
270   }
271 
272   if(gGui->osd.info.visible) {
273     gGui->osd.info.visible--;
274     if(!gGui->osd.info.visible) {
275       xine_osd_hide(gGui->osd.info.osd[0], 0);
276     }
277   }
278 
279   pthread_mutex_unlock(&osd_mutex);
280 }
281 
osd_stream_infos(void)282 void osd_stream_infos(void) {
283 
284   if(gGui->osd.enabled) {
285     int         vwidth, vheight, asrate;
286     int         wwidth, wheight;
287     const char *vcodec, *acodec;
288     char        buffer[256], *p;
289     int         x, y;
290     int         w, h, osdw;
291     int         playedtime, playeddays, totaltime, pos;
292     int         audiochannel, spuchannel, len;
293 
294     vcodec       = xine_get_meta_info(gGui->stream, XINE_META_INFO_VIDEOCODEC);
295     acodec       = xine_get_meta_info(gGui->stream, XINE_META_INFO_AUDIOCODEC);
296     vwidth       = xine_get_stream_info(gGui->stream, XINE_STREAM_INFO_VIDEO_WIDTH);
297     vheight      = xine_get_stream_info(gGui->stream, XINE_STREAM_INFO_VIDEO_HEIGHT);
298     asrate       = xine_get_stream_info(gGui->stream, XINE_STREAM_INFO_AUDIO_SAMPLERATE);
299     audiochannel = xine_get_param(gGui->stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL);
300     spuchannel   = xine_get_param(gGui->stream, XINE_PARAM_SPU_CHANNEL);
301 
302     if(!gui_xine_get_pos_length(gGui->stream, &pos, &playedtime, &totaltime))
303       return;
304 
305     playedtime /= 1000;
306     totaltime  /= 1000;
307 
308     xine_osd_clear(gGui->osd.sinfo.osd[0]);
309 
310     /* We're in visual animation mode */
311     if((vwidth == 0) && (vheight == 0)) {
312       if(gGui->visual_anim.running) {
313 	if(gGui->visual_anim.enabled == 1) {
314           video_window_get_frame_size (gGui->vwin, &vwidth, &vheight);
315 	  vcodec = _("post animation");
316 	}
317 	else if(gGui->visual_anim.enabled == 2) {
318 	  vcodec  = xine_get_meta_info(gGui->visual_anim.stream, XINE_META_INFO_VIDEOCODEC);
319 	  vwidth  = xine_get_stream_info(gGui->visual_anim.stream, XINE_STREAM_INFO_VIDEO_WIDTH);
320 	  vheight = xine_get_stream_info(gGui->visual_anim.stream, XINE_STREAM_INFO_VIDEO_HEIGHT);
321 	}
322       }
323       else {
324         video_window_get_frame_size (gGui->vwin, &vwidth, &vheight);
325 	vcodec = _("unknown");
326       }
327     }
328 
329     _osd_get_output_size(&wwidth, &wheight);
330 
331     y = x = 0;
332 
333     pthread_mutex_lock (&gGui->mmk_mutex);
334     strlcpy(buffer, (gGui->is_display_mrl) ? gGui->mmk.mrl : gGui->mmk.ident, sizeof(buffer));
335     pthread_mutex_unlock (&gGui->mmk_mutex);
336     xine_osd_get_text_size(gGui->osd.sinfo.osd[0], buffer, &osdw, &h);
337     p = buffer;
338     while(osdw > (wwidth - 40)) {
339       *(p++) = '\0';
340       *(p)   = '.';
341       *(p+1) = '.';
342       *(p+2) = '.';
343       xine_osd_get_text_size(gGui->osd.sinfo.osd[0], p, &osdw, &h);
344     }
345     xine_osd_draw_text(gGui->osd.sinfo.osd[0], x, y, p, XINE_OSD_TEXT1);
346 
347     y += h;
348 
349     if(vcodec && vwidth && vheight) {
350       snprintf(buffer, sizeof(buffer), "%s: %dX%d", vcodec, vwidth, vheight);
351       xine_osd_draw_text(gGui->osd.sinfo.osd[0], x, y, buffer, XINE_OSD_TEXT1);
352       xine_osd_get_text_size(gGui->osd.sinfo.osd[0], buffer, &w, &h);
353       if(w > osdw)
354 	osdw = w;
355       y += h;
356     }
357 
358     if(acodec && asrate) {
359       snprintf(buffer, sizeof(buffer), "%s: %d%s", acodec, asrate, "Hz");
360       xine_osd_draw_text(gGui->osd.sinfo.osd[0], x, y, buffer, XINE_OSD_TEXT1);
361       xine_osd_get_text_size(gGui->osd.sinfo.osd[0], buffer, &w, &h);
362       if(w > osdw)
363 	osdw = w;
364       y += h;
365     }
366 
367     strlcpy(buffer, _("Audio: "), sizeof(buffer));
368     len = strlen(buffer);
369     switch(audiochannel) {
370     case -2:
371       strlcat(buffer, "off", sizeof(buffer));
372       break;
373     case -1:
374       if(!xine_get_audio_lang (gGui->stream, audiochannel, &buffer[len]))
375 	strlcat(buffer, "auto", sizeof(buffer));
376       break;
377     default:
378       if(!xine_get_audio_lang (gGui->stream, audiochannel, &buffer[len]))
379 	snprintf(buffer+strlen(buffer), sizeof(buffer)-strlen(buffer), "%3d", audiochannel);
380       break;
381     }
382 
383     strlcat(buffer, ", Spu: ", sizeof(buffer));
384     len = strlen(buffer);
385     switch (spuchannel) {
386     case -2:
387       strlcat(buffer, "off", sizeof(buffer));
388       break;
389     case -1:
390       if(!xine_get_spu_lang (gGui->stream, spuchannel, &buffer[len]))
391 	strlcat(buffer, "auto", sizeof(buffer));
392       break;
393     default:
394       if(!xine_get_spu_lang (gGui->stream, spuchannel, &buffer[len]))
395         snprintf(buffer+strlen(buffer), sizeof(buffer)-strlen(buffer), "%3d", spuchannel);
396       break;
397     }
398     strlcat(buffer, ".", sizeof(buffer));
399     xine_osd_draw_text(gGui->osd.sinfo.osd[0], x, y, buffer, XINE_OSD_TEXT1);
400     xine_osd_get_text_size(gGui->osd.sinfo.osd[0], buffer, &w, &h);
401     if(w > osdw)
402       osdw = w;
403 
404     y += (h);
405 
406     playeddays = playedtime / (3600 * 24);
407 
408     if(playeddays > 0)
409       sprintf(buffer, "%d::%02d ", playeddays, playedtime / 3600);
410     else
411       sprintf(buffer, "%d:%02d:%02d ", playedtime / 3600, (playedtime % 3600) / 60, playedtime % 60);
412 
413     if(totaltime > 0) {
414       int totaldays;
415 
416       totaldays  = totaltime / (3600 * 24);
417       sprintf(buffer+strlen(buffer), "(%.0f%%) %s ", ((float)playedtime / (float)totaltime) * 100, _("of"));
418 
419       if(totaldays > 0)
420 	sprintf(buffer+strlen(buffer), "%d::%02d", totaldays, totaltime / 3600);
421       else
422 	sprintf(buffer+strlen(buffer), "%d:%02d:%02d", totaltime / 3600, (totaltime % 3600) / 60, totaltime % 60);
423     }
424 
425     xine_osd_draw_text(gGui->osd.sinfo.osd[0], x, y, buffer, XINE_OSD_TEXT1);
426     xine_osd_get_text_size(gGui->osd.sinfo.osd[0], buffer, &w, &h);
427     if(w > osdw)
428       osdw = w;
429 
430     osd_stream_position(pos);
431 
432     x = (wwidth - osdw) - 40;
433     xine_osd_set_position(gGui->osd.sinfo.osd[0], (x >= 0) ? x : 0, 15);
434     gGui->osd.sinfo.x = (x >= 0) ? x : 0;
435     gGui->osd.sinfo.y = 15;
436     gGui->osd.sinfo.w = osdw;
437 
438     pthread_mutex_lock(&osd_mutex);
439     _xine_osd_show(gGui->osd.sinfo.osd[0], 0);
440     gGui->osd.sinfo.visible = gGui->osd.timeout;
441     pthread_mutex_unlock(&osd_mutex);
442   }
443 }
444 
osd_draw_bar(const char * title,int min,int max,int val,int type)445 void osd_draw_bar(const char *title, int min, int max, int val, int type) {
446 
447   if(gGui->osd.enabled) {
448     int      wwidth, wheight;
449     int      bar_color[40];
450     int      i, x;
451     float    _val = (int) val;
452     float    _min = (int) min;
453     float    _max = (int) max;
454     int      pos;
455 
456     if(max <= min)
457       _max = (int) (min + 1);
458     if(min >= max)
459       _min = (int) (max - 1);
460     if(val > max)
461       _val = (int) max;
462     if(val < min)
463       _val = (int) min;
464 
465     pos = (int) (_val + -_min) / ((_max + -_min) / 40);
466 
467     _osd_get_output_size(&wwidth, &wheight);
468 
469     xine_osd_clear(gGui->osd.bar.osd[0]);
470     xine_osd_clear(gGui->osd.bar.osd[1]);
471 
472     memset(&bar_color, (XINE_OSD_TEXT1 + 7), sizeof(int) * 40);
473 
474     switch(type) {
475     case OSD_BAR_PROGRESS:
476     case OSD_BAR_STEPPER:
477       if(pos)
478 	memset(bar_color, (XINE_OSD_TEXT1 + 21), sizeof(int) * pos);
479       break;
480     case OSD_BAR_POS:
481     case OSD_BAR_POS2:
482       if(pos)
483 	bar_color[pos - 1] = (XINE_OSD_TEXT1 + 21);
484       break;
485     }
486 
487     if((type == OSD_BAR_PROGRESS) || (type == OSD_BAR_POS)) {
488       x = 3;
489       xine_osd_draw_rect(gGui->osd.bar.osd[0], x, 2, x + 3, BAR_HEIGHT - 2, XINE_OSD_TEXT1 + 9, 1);
490       x += 8;
491 
492       for(i = 0; i < 40; i++, x += 8) {
493 	xine_osd_draw_rect(gGui->osd.bar.osd[0],
494 			   x, 6, x + 3, BAR_HEIGHT - 2, bar_color[i], 1);
495       }
496 
497       xine_osd_draw_rect(gGui->osd.bar.osd[0],
498 			 x, 2, x + 3, BAR_HEIGHT - 2, XINE_OSD_TEXT1 + 9, 1);
499     }
500     else if(type == OSD_BAR_POS2) {
501       x = 3;
502       xine_osd_draw_rect(gGui->osd.bar.osd[0], x, 2, x + 3, BAR_HEIGHT - 2, XINE_OSD_TEXT1 + 9, 1);
503       x += 8;
504 
505       for(i = 0; i < 40; i++, x += 8) {
506 	if(i == (pos - 1))
507 	  xine_osd_draw_rect(gGui->osd.bar.osd[0],
508 			     x, 2, x + 3, BAR_HEIGHT - 2, bar_color[i], 1);
509 	else
510 	  xine_osd_draw_rect(gGui->osd.bar.osd[0],
511 			     x, 6, x + 3, BAR_HEIGHT - 6, bar_color[i], 1);
512       }
513 
514       xine_osd_draw_rect(gGui->osd.bar.osd[0],
515 			 x, 2, x + 3, BAR_HEIGHT - 2, XINE_OSD_TEXT1 + 9, 1);
516     }
517     else if(type == OSD_BAR_STEPPER) {
518       int y = BAR_HEIGHT - 4;
519       int step = y / 20;
520 
521       x = 11;
522 
523       for(i = 0; i < 40; i++, x += 8) {
524 	xine_osd_draw_rect(gGui->osd.bar.osd[0],
525 			   x, y, x + 3, BAR_HEIGHT - 2, bar_color[i], 1);
526 
527 	if(!(i % 2))
528 	  y -= step;
529 
530       }
531     }
532 
533     if(title) {
534       int  tw, th;
535 
536       gGui->osd.bar.have_text = 1;
537 
538       xine_osd_get_text_size(gGui->osd.bar.osd[1], title, &tw, &th);
539       xine_osd_draw_text(gGui->osd.bar.osd[1], (BAR_WIDTH - tw) >> 1, 0, title, XINE_OSD_TEXT1);
540     }
541     else
542       gGui->osd.bar.have_text = 0;
543 
544     x = (wwidth - BAR_WIDTH) >> 1;
545     xine_osd_set_position(gGui->osd.bar.osd[0], (x >= 0) ? x : 0, (wheight - BAR_HEIGHT) - 40);
546     xine_osd_set_position(gGui->osd.bar.osd[1], (x >= 0) ? x : 0, (wheight - (BAR_HEIGHT * 2)) - 40);
547 
548     /* don't even bother drawing osd over those small streams.
549      * it would look pretty bad.
550      */
551     if( wwidth > MINIMUM_WIN_WIDTH ) {
552       pthread_mutex_lock(&osd_mutex);
553       _xine_osd_show(gGui->osd.bar.osd[0], 0);
554       if(title)
555         _xine_osd_show(gGui->osd.bar.osd[1], 0);
556       gGui->osd.bar.visible = gGui->osd.timeout;
557       pthread_mutex_unlock(&osd_mutex);
558     }
559   }
560 }
561 
osd_stream_position(int pos)562 void osd_stream_position(int pos) {
563   osd_draw_bar(_("Position in Stream"), 0, 65535, pos, OSD_BAR_POS2);
564 }
565 
osd_display_info(const char * info,...)566 void osd_display_info(const char *info, ...) {
567 
568   if(gGui->osd.enabled && !gGui->on_quit) {
569     va_list   args;
570     char     *buf;
571 
572     va_start(args, info);
573     buf = xitk_vasprintf(info, args);
574     va_end(args);
575 
576     if (!buf)
577       return;
578 
579     xine_osd_clear(gGui->osd.info.osd[0]);
580 
581     xine_osd_draw_text(gGui->osd.info.osd[0], 0, 0, buf, XINE_OSD_TEXT1);
582     xine_osd_set_position(gGui->osd.info.osd[0], 20, 10 + 30);
583 
584     pthread_mutex_lock(&osd_mutex);
585     _xine_osd_show(gGui->osd.info.osd[0], 0);
586     gGui->osd.info.visible = gGui->osd.timeout;
587     pthread_mutex_unlock(&osd_mutex);
588 
589     free(buf);
590   }
591 }
592 
osd_update_status(void)593 void osd_update_status(void) {
594 
595   if(gGui->osd.enabled) {
596     int  status;
597     char buffer[256];
598     int wwidth, wheight;
599 
600     status = xine_get_status(gGui->stream);
601 
602     xine_osd_clear(gGui->osd.status.osd[0]);
603 
604     /*
605       { : eject
606       [ : previous
607       | : | (thin)
608       @ : | (large)
609       ] : next
610       } : stop
611       $ : > (large)
612       > : play
613       < : pause
614     */
615 
616     memset(&buffer, 0, sizeof(buffer));
617 
618     switch(status) {
619     case XINE_STATUS_IDLE:
620     case XINE_STATUS_STOP:
621       strlcpy(buffer, _osd_get_status_sym(status), sizeof(buffer));
622       break;
623 
624     case XINE_STATUS_PLAY:
625       {
626 	int speed = xine_get_param(gGui->stream, XINE_PARAM_SPEED);
627 	int secs;
628 
629 	if(gui_xine_get_pos_length(gGui->stream, NULL, &secs, NULL)) {
630 	  secs /= 1000;
631 
632 	  snprintf(buffer, sizeof(buffer), "%s %02d:%02d:%02d", (_osd_get_speed_sym(speed)),
633 		   secs / (60*60), (secs / 60) % 60, secs % 60);
634 	}
635 	else
636 	  strlcpy(buffer, _osd_get_speed_sym(speed), sizeof(buffer));
637 
638       }
639       break;
640 
641     case XINE_STATUS_QUIT:
642       /* noop */
643       break;
644     }
645 
646     if( gGui->osd.use_unscaled && gGui->osd.unscaled_available )
647       xine_osd_set_font(gGui->osd.status.osd[0], "cetus", UNSCALED_FONT_SIZE);
648     else
649       xine_osd_set_font(gGui->osd.status.osd[0], "cetus", FONT_SIZE);
650 
651     /* set latin1 encoding (NULL) for status text with special characters,
652      * then switch back to locale encoding ("")
653      */
654     xine_osd_set_encoding(gGui->osd.status.osd[0], NULL);
655     xine_osd_draw_text(gGui->osd.status.osd[0], 0, 0, buffer, XINE_OSD_TEXT1);
656     xine_osd_set_encoding(gGui->osd.status.osd[0], "");
657     xine_osd_set_position(gGui->osd.status.osd[0], 20, 10);
658 
659     _osd_get_output_size(&wwidth, &wheight);
660 
661     /* don't even bother drawing osd over those small streams.
662      * it would look pretty bad.
663      */
664     if( wwidth > MINIMUM_WIN_WIDTH ) {
665       pthread_mutex_lock(&osd_mutex);
666       _xine_osd_show(gGui->osd.status.osd[0], 0);
667       gGui->osd.status.visible = gGui->osd.timeout;
668       pthread_mutex_unlock(&osd_mutex);
669     }
670   }
671 }
672 
osd_display_spu_lang(void)673 void osd_display_spu_lang(void) {
674   char   buffer[XINE_LANG_MAX+128];
675   char   lang_buffer[XINE_LANG_MAX];
676   int    channel;
677   const char *lang = NULL;
678 
679   channel = xine_get_param(gGui->stream, XINE_PARAM_SPU_CHANNEL);
680 
681   switch(channel) {
682   case -2:
683     lang = "off";
684     break;
685   case -1:
686     if(!xine_get_spu_lang(gGui->stream, channel, &lang_buffer[0]))
687       lang = "auto";
688     else
689       lang = lang_buffer;
690     break;
691   default:
692     if(!xine_get_spu_lang(gGui->stream, channel, &lang_buffer[0]))
693       snprintf(lang_buffer, sizeof(lang_buffer), "%3d", channel);
694     lang = lang_buffer;
695     break;
696   }
697 
698   snprintf(buffer, sizeof(buffer), "%s%s", _("Subtitles: "), get_language_from_iso639_1(lang));
699   osd_display_info("%s", buffer);
700 }
701 
osd_display_audio_lang(void)702 void osd_display_audio_lang(void) {
703   char   buffer[XINE_LANG_MAX+128];
704   char   lang_buffer[XINE_LANG_MAX];
705   int    channel;
706   const char *lang = NULL;
707 
708   channel = xine_get_param(gGui->stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL);
709 
710   switch(channel) {
711   case -2:
712     lang = "off";
713     break;
714   case -1:
715     if(!xine_get_audio_lang(gGui->stream, channel, &lang_buffer[0]))
716       lang = "auto";
717     else
718       lang = lang_buffer;
719     break;
720   default:
721     if(!xine_get_audio_lang(gGui->stream, channel, &lang_buffer[0]))
722       snprintf(lang_buffer, sizeof(lang_buffer), "%3d", channel);
723     lang = lang_buffer;
724     break;
725   }
726 
727   snprintf(buffer, sizeof(buffer), "%s%s", _("Audio Channel: "), get_language_from_iso639_1(lang));
728   osd_display_info("%s", buffer);
729 }
730 
osd_update_osd(void)731 void osd_update_osd(void) {
732   int vwidth, vheight, wwidth, wheight;
733   int x;
734 
735   if(!gGui->osd.sinfo.visible && !gGui->osd.bar.visible)
736     return;
737 
738   vwidth  = xine_get_stream_info(gGui->stream, XINE_STREAM_INFO_VIDEO_WIDTH);
739   vheight = xine_get_stream_info(gGui->stream, XINE_STREAM_INFO_VIDEO_HEIGHT);
740 
741   if((vwidth == 0) && (vheight == 0)) {
742     if(gGui->visual_anim.running) {
743       if(gGui->visual_anim.enabled == 1)
744         video_window_get_frame_size (gGui->vwin, &vwidth, &vheight);
745       else if(gGui->visual_anim.enabled == 2)
746 	vwidth  = xine_get_stream_info(gGui->visual_anim.stream, XINE_STREAM_INFO_VIDEO_WIDTH);
747     }
748     else
749       video_window_get_frame_size (gGui->vwin, &vwidth, &vheight);
750 
751   }
752 
753   _osd_get_output_size(&wwidth, &wheight);
754 
755   pthread_mutex_lock(&osd_mutex);
756 
757   if(gGui->osd.sinfo.visible) {
758     xine_osd_hide(gGui->osd.sinfo.osd[0], 0);
759 
760     x = (wwidth - gGui->osd.sinfo.w) - 40;
761     xine_osd_set_position(gGui->osd.sinfo.osd[0], (x >= 0) ? x : 0,  gGui->osd.sinfo.y);
762     _xine_osd_show(gGui->osd.sinfo.osd[0], 0);
763   }
764 
765   if(gGui->osd.bar.visible) {
766     xine_osd_hide(gGui->osd.bar.osd[0], 0);
767     xine_osd_hide(gGui->osd.bar.osd[1], 0);
768 
769     x = (wwidth - BAR_WIDTH) >> 1;
770     xine_osd_set_position(gGui->osd.bar.osd[0], (x >= 0) ? x : 0, (wheight - BAR_HEIGHT) - 40);
771     xine_osd_set_position(gGui->osd.bar.osd[1], (x >= 0) ? x : 0, (wheight - (BAR_HEIGHT * 2)) - 40);
772 
773     /* don't even bother drawing osd over those small streams.
774      * it would look pretty bad.
775      */
776     if(wwidth > MINIMUM_WIN_WIDTH) {
777       _xine_osd_show(gGui->osd.bar.osd[0], 0);
778 
779       if(gGui->osd.bar.have_text)
780 	_xine_osd_show(gGui->osd.bar.osd[1], 0);
781     }
782 
783   }
784 
785   pthread_mutex_unlock(&osd_mutex);
786 }
787