]> git.sesse.net Git - vlc/blob - src/interface/intf_msg.c
7fb09521b1ea317ad7c3819dfc8ba7a17c200820
[vlc] / src / interface / intf_msg.c
1 /*****************************************************************************
2  * intf_msg.c: messages interface
3  * This library provides basic functions for threads to interact with user
4  * interface, such as message output. See config.h for output configuration.
5  *****************************************************************************
6  * Copyright (C) 1998, 1999, 2000 VideoLAN
7  *
8  * Authors:
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public
21  * License along with this program; if not, write to the
22  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <errno.h>                                                  /* errno */
30 #include <fcntl.h>                     /* O_CREAT, O_TRUNC, O_WRONLY, O_SYNC */
31 #include <stdio.h>                                               /* required */
32 #include <stdarg.h>                                       /* va_list for BSD */
33 #include <stdlib.h>                                              /* malloc() */
34 #include <string.h>                                            /* strerror() */
35 #include <unistd.h>                                      /* close(), write() */
36
37 #include "config.h"
38 #include "common.h"
39 #include "mtime.h"
40 #include "threads.h"
41 #include "intf_msg.h"
42 #include "interface.h"
43 #include "intf_console.h"
44
45 #include "main.h"
46
47 /*****************************************************************************
48  * intf_msg_item_t
49  *****************************************************************************
50  * Store a single message. Messages have a maximal size of INTF_MSG_MSGSIZE.
51  * If DEBUG is defined, messages have a date field and debug messages are
52  * printed with a date to allow more precise profiling.
53  *****************************************************************************/
54 typedef struct
55 {
56     int     i_type;                               /* message type, see below */
57     char *  psz_msg;                                   /* the message itself */
58
59 #ifdef DEBUG
60     /* Debugging informations - in DEBUG mode, debug messages have calling
61      * location informations printed */
62     mtime_t date;                                     /* date of the message */
63     char *  psz_file;               /* file in which the function was called */
64     char *  psz_function;     /* function from which the function was called */
65     int     i_line;                 /* line at which the function was called */
66 #endif
67 } intf_msg_item_t;
68
69 /* Message types */
70 #define INTF_MSG_STD    0                                /* standard message */
71 #define INTF_MSG_ERR    1                                   /* error message */
72 #define INTF_MSG_INTF   2                               /* interface message */
73 #define INTF_MSG_DBG    3                                   /* debug message */
74
75 /*****************************************************************************
76  * intf_msg_t
77  *****************************************************************************
78  * Store all data requiered by messages interfaces. It has a single reference
79  * int p_main.
80  *****************************************************************************/
81 typedef struct intf_msg_s
82 {
83 #ifdef INTF_MSG_QUEUE
84     /* Message queue */
85     vlc_mutex_t             lock;                      /* message queue lock */
86     int                     i_count;            /* number of messages stored */
87     intf_msg_item_t         msg[INTF_MSG_QSIZE];            /* message queue */
88 #endif
89
90 #ifdef DEBUG_LOG
91     /* Log file */
92     int                     i_log_file;                          /* log file */
93 #endif
94
95 #if !defined(INTF_MSG_QUEUE) && !defined(DEBUG_LOG)
96     /* If neither messages queue, neither log file is used, then the structure
97      * is empty. However, empty structures are not allowed in C. Therefore, a
98      * dummy integer is used to fill it. */
99     int                     i_dummy;                        /* unused filler */
100 #endif
101 } intf_msg_t;
102
103 /*****************************************************************************
104  * Local prototypes
105  *****************************************************************************/
106
107 static void QueueMsg        ( intf_msg_t *p_msg, int i_type,
108                               char *psz_format, va_list ap );
109 static void PrintMsg        ( intf_msg_item_t *p_msg );
110 #ifdef DEBUG
111 static void QueueDbgMsg     ( intf_msg_t *p_msg, char *psz_file,
112                               char *psz_function, int i_line,
113                               char *psz_format, va_list ap );
114 #endif
115 #ifdef INTF_MSG_QUEUE
116 static void FlushLockedMsg  ( intf_msg_t *p_msg );
117 #endif
118
119
120 /*****************************************************************************
121  * intf_MsgCreate: initialize messages interface                         (ok ?)
122  *****************************************************************************
123  * This functions has to be called before any call to other intf_*Msg functions.
124  * It set up the locks and the message queue if it is used.
125  *****************************************************************************/
126 p_intf_msg_t intf_MsgCreate( void )
127 {
128     p_intf_msg_t p_msg;
129
130     /* Allocate structure */
131     p_msg = malloc( sizeof( intf_msg_t ) );
132     if( p_msg == NULL )
133     {
134         errno = ENOMEM;
135     }
136     else
137     {
138 #ifdef INTF_MSG_QUEUE
139     /* Message queue initialization */
140     vlc_mutex_init( &p_msg->lock );                        /* intialize lock */
141     p_msg->i_count = 0;                                    /* queue is empty */
142 #endif
143
144 #ifdef DEBUG_LOG
145         /* Log file initialization - on failure, file pointer will be null,
146          * and no log will be issued, but this is not considered as an
147          * error */
148         p_msg->i_log_file = open( DEBUG_LOG, O_CREAT | O_TRUNC |
149 #ifndef SYS_BSD
150                                   O_SYNC |
151 #else
152                                   O_ASYNC |
153 #endif /* SYS_BSD */
154                                   O_WRONLY, 0666 );
155
156 #endif
157     }
158     return( p_msg );
159 }
160
161 /*****************************************************************************
162  * intf_MsgDestroy: free resources allocated by intf_InitMsg            (ok ?)
163  *****************************************************************************
164  * This functions prints all messages remaining in queue, then free all the
165  * resources allocated by intf_InitMsg.
166  * No other messages interface functions should be called after this one.
167  *****************************************************************************/
168 void intf_MsgDestroy( void )
169 {
170     intf_FlushMsg();                         /* print all remaining messages */
171
172 #ifdef DEBUG_LOG
173     /* Close log file if any */
174     if( p_main->p_msg->i_log_file >= 0 )
175     {
176         close( p_main->p_msg->i_log_file );
177     }
178 #endif
179
180     /* Free structure */
181     free( p_main->p_msg );
182 }
183
184 /*****************************************************************************
185  * intf_Msg: print a message                                              (ok ?)
186  *****************************************************************************
187  * This function queue a message for later printing, or print it immediately
188  * if the queue isn't used.
189  *****************************************************************************/
190 void intf_Msg( char *psz_format, ... )
191 {
192     va_list ap;
193
194     va_start( ap, psz_format );
195     QueueMsg( p_main->p_msg, INTF_MSG_STD, psz_format, ap );
196     va_end( ap );
197 }
198
199 /*****************************************************************************
200  * intf_ErrMsg : print an error message                                   (ok ?)
201  *****************************************************************************
202  * This function is the same as intf_Msg, except that it prints its messages
203  * on stderr.
204  *****************************************************************************/
205 void intf_ErrMsg( char *psz_format, ...)
206 {
207     va_list ap;
208
209     va_start( ap, psz_format );
210     QueueMsg( p_main->p_msg, INTF_MSG_ERR, psz_format, ap );
211     va_end( ap );
212 }
213
214 /*****************************************************************************
215  * intf_IntfMsg : print an interface message                              (ok ?)
216  *****************************************************************************
217  * In opposition to all other intf_*Msg function, this function does not print
218  * it's message on default terminal (stdout or stderr), but send it to
219  * interface (in fact to the X11 console). This means that the interface MUST
220  * be initialized and a X11 console openned before this function is used, and
221  * that once the console is closed, this call is vorbidden.
222  * Practically, only the interface thread itself should call this function, and
223  * flush all messages before intf_CloseX11Console() is called.
224  *****************************************************************************/
225 void intf_IntfMsg(char *psz_format, ...)
226 {
227     va_list ap;
228
229     va_start( ap, psz_format );
230     QueueMsg( p_main->p_msg, INTF_MSG_INTF, psz_format, ap );
231     va_end( ap );
232 }
233
234 /*****************************************************************************
235  * _intf_DbgMsg: print a debugging message                                (ok ?)
236  *****************************************************************************
237  * This function prints a debugging message. Compared to other intf_*Msg
238  * functions, it is only defined if DEBUG is defined and require a file name,
239  * a function name and a line number as additionnal debugging informations. It
240  * also prints a debugging header for each received line.
241  *****************************************************************************/
242 #ifdef DEBUG
243 void _intf_DbgMsg( char *psz_file, char *psz_function, int i_line,
244                    char *psz_format, ...)
245 {
246     va_list ap;
247
248     va_start( ap, psz_format );
249     QueueDbgMsg( p_main->p_msg, psz_file, psz_function, i_line,
250                  psz_format, ap );
251     va_end( ap );
252 }
253 #endif
254
255 /*****************************************************************************
256  * intf_ErrMsgImm: print a message                                        (ok ?)
257  *****************************************************************************
258  * This function prints a message immediately. If the queue is used, all
259  * waiting messages are also printed.
260  *****************************************************************************/
261 void intf_MsgImm( char *psz_format, ... )
262 {
263     va_list ap;
264
265     va_start( ap, psz_format );
266     QueueMsg( p_main->p_msg, INTF_MSG_STD, psz_format, ap );
267     va_end( ap );
268     intf_FlushMsg();
269 }
270
271 /*****************************************************************************
272  * intf_ErrMsgImm: print an error message immediately                     (ok ?)
273  *****************************************************************************
274  * This function is the same as intf_MsgImm, except that it prints its message
275  * on stderr.
276  *****************************************************************************/
277 void intf_ErrMsgImm(char *psz_format, ...)
278 {
279     va_list ap;
280
281     va_start( ap, psz_format );
282     QueueMsg( p_main->p_msg, INTF_MSG_ERR, psz_format, ap );
283     va_end( ap );
284     intf_FlushMsg();
285 }
286
287 /*****************************************************************************
288  * _intf_DbgMsgImm: print a debugging message immediately                 (ok ?)
289  *****************************************************************************
290  * This function is the same as intf_DbgMsgImm, except that it prints its
291  * message immediately. It should only be called through the macro
292  * intf_DbgMsgImm().
293  *****************************************************************************/
294 #ifdef DEBUG
295 void _intf_DbgMsgImm( char *psz_file, char *psz_function, int i_line,
296                       char *psz_format, ...)
297 {
298     va_list ap;
299
300     va_start( ap, psz_format );
301     QueueDbgMsg( p_main->p_msg, psz_file, psz_function, i_line,
302                  psz_format, ap );
303     va_end( ap );
304     intf_FlushMsg();
305 }
306 #endif
307
308 /*****************************************************************************
309  * intf_FlushMsg                                                          (ok ?)
310  *****************************************************************************
311  * Print all messages remaining in queue: get lock and call FlushLockedMsg.
312  * This function does nothing if the message queue isn't used.
313  * This function is only implemented if message queue is used. If not, it is an
314  * empty macro.
315  *****************************************************************************/
316 #ifdef INTF_MSG_QUEUE
317 void intf_FlushMsg( void )
318 {
319     vlc_mutex_lock( &p_main->p_msg->lock );                      /* get lock */
320     FlushLockedMsg( p_main->p_msg );                       /* flush messages */
321     vlc_mutex_unlock( &p_main->p_msg->lock );              /* give lock back */
322 }
323 #endif
324
325 /* following functions are local */
326
327 /*****************************************************************************
328  * QueueMsg: add a message to a queue
329  *****************************************************************************
330  * This function provide basic functionnalities to other intf_*Msg functions.
331  * It add a message to a queue (after having printed all stored messages if it
332  * is full. If the message can't be converted to string in memory, it exit the
333  * program. If the queue is not used, it prints the message immediately.
334  *****************************************************************************/
335 static void QueueMsg( intf_msg_t *p_msg, int i_type, char *psz_format, va_list ap )
336 {
337     char *                  psz_str;             /* formatted message string */
338     intf_msg_item_t *       p_msg_item;                /* pointer to message */
339
340 #ifndef INTF_MSG_QUEUE /*..................................... instant mode ...*/
341     intf_msg_item_t         msg_item;                             /* message */
342     p_msg_item =           &msg_item;
343 #endif /*......................................................................*/
344
345     /*
346      * Convert message to string
347      */
348     vasprintf( &psz_str, psz_format, ap );
349     if( psz_str == NULL )
350     {
351         fprintf(stderr, "warning: can't store following message (%s): ",
352                 strerror(errno) );
353         vfprintf(stderr, psz_format, ap );
354         exit( errno );
355     }
356
357 #ifdef INTF_MSG_QUEUE /*........................................ queue mode ...*/
358     vlc_mutex_lock( &p_msg->lock );                            /* get lock */
359     if( p_msg->i_count == INTF_MSG_QSIZE )          /* flush queue if needed */
360     {
361 #ifdef DEBUG               /* in debug mode, queue overflow causes a warning */
362         fprintf(stderr, "warning: message queue overflow\n" );
363 #endif
364         FlushLockedMsg( p_msg );
365     }
366     p_msg_item = p_msg->msg + p_msg->i_count++;            /* select message */
367 #endif /*................................................ end of queue mode ...*/
368
369     /*
370      * Fill message information fields
371      */
372     p_msg_item->i_type =     i_type;
373     p_msg_item->psz_msg =    psz_str;
374
375 #ifdef INTF_MSG_QUEUE /*........................................... queue mode */
376     vlc_mutex_unlock( &p_msg->lock );                      /* give lock back */
377 #else /*......................................................... instant mode */
378     PrintMsg( p_msg_item );                                 /* print message */
379     free( psz_str );                                    /* free message data */
380 #endif /*......................................................................*/
381 }
382
383 /*****************************************************************************
384  * QueueDbgMsg: add a message to a queue with debugging informations
385  *****************************************************************************
386  * This function is the same as QueueMsg, except that it is only defined when
387  * DEBUG is define, and require additionnal debugging informations.
388  *****************************************************************************/
389 #ifdef DEBUG
390 static void QueueDbgMsg(intf_msg_t *p_msg, char *psz_file, char *psz_function,
391                         int i_line, char *psz_format, va_list ap)
392 {
393     char *                  psz_str;             /* formatted message string */
394     intf_msg_item_t *       p_msg_item;                /* pointer to message */
395
396 #ifndef INTF_MSG_QUEUE /*..................................... instant mode ...*/
397     intf_msg_item_t         msg_item;                             /* message */
398     p_msg_item =           &msg_item;
399 #endif /*......................................................................*/
400
401     /*
402      * Convert message to string
403      */
404     vasprintf( &psz_str, psz_format, ap );
405     if( psz_str == NULL )
406     {
407         fprintf(stderr, "warning: can't store following message (%s): ",
408                 strerror(errno) );
409         fprintf(stderr, INTF_MSG_DBG_FORMAT, psz_file, psz_function, i_line );
410         vfprintf(stderr, psz_format, ap );
411         exit( errno );
412     }
413
414 #ifdef INTF_MSG_QUEUE /*........................................ queue mode ...*/
415     vlc_mutex_lock( &p_msg->lock );                            /* get lock */
416     if( p_msg->i_count == INTF_MSG_QSIZE )          /* flush queue if needed */
417     {
418 #ifdef DEBUG               /* in debug mode, queue overflow causes a warning */
419         fprintf(stderr, "warning: message queue overflow\n" );
420 #endif
421         FlushLockedMsg( p_msg );
422     }
423     p_msg_item = p_msg->msg + p_msg->i_count++;            /* select message */
424 #endif /*................................................ end of queue mode ...*/
425
426     /*
427      * Fill message information fields
428      */
429     p_msg_item->i_type =       INTF_MSG_DBG;
430     p_msg_item->psz_msg =      psz_str;
431     p_msg_item->psz_file =     psz_file;
432     p_msg_item->psz_function = psz_function;
433     p_msg_item->i_line =       i_line;
434     p_msg_item->date =         mdate();
435
436 #ifdef INTF_MSG_QUEUE /*........................................... queue mode */
437     vlc_mutex_unlock( &p_msg->lock );                      /* give lock back */
438 #else /*......................................................... instant mode */
439     PrintMsg( p_msg_item );                                 /* print message */
440     free( psz_str );                                    /* free message data */
441 #endif /*......................................................................*/
442 }
443 #endif
444
445 /*****************************************************************************
446  * FlushLockedMsg                                                        (ok ?)
447  *****************************************************************************
448  * Print all messages remaining in queue. MESSAGE QUEUE MUST BE LOCKED, since
449  * this function does not check the lock. This function is only defined if
450  * INTF_MSG_QUEUE is defined.
451  *****************************************************************************/
452 #ifdef INTF_MSG_QUEUE
453 static void FlushLockedMsg ( intf_msg_t *p_msg )
454 {
455     int i_index;
456
457     for( i_index = 0; i_index < p_msg->i_count; i_index++ )
458     {
459         /* Print message and free message data */
460         PrintMsg( &p_msg->msg[i_index] );
461         free( p_msg->msg[i_index].psz_msg );
462     }
463
464     p_msg->i_count = 0;
465 }
466 #endif
467
468 /*****************************************************************************
469  * PrintMsg: print a message                                              (ok ?)
470  *****************************************************************************
471  * Print a single message. The message data is not freed. This function exists
472  * in two version. The DEBUG version prints a date with each message, and is
473  * able to log messages (if DEBUG_LOG is defined).
474  * The normal one just prints messages to the screen.
475  *****************************************************************************/
476 #ifdef DEBUG
477
478 static void PrintMsg( intf_msg_item_t *p_msg )
479 {
480     char    psz_date[MSTRTIME_MAX_SIZE];            /* formatted time buffer */
481     char *  psz_msg;                                       /* message buffer */
482
483     /* Format message - the message is formatted here because in case the log
484      * file is used, it avoids another format string parsing */
485     switch( p_msg->i_type )
486     {
487     case INTF_MSG_STD:                                   /* regular messages */
488     case INTF_MSG_ERR:
489         asprintf( &psz_msg, "%s", p_msg->psz_msg );
490         break;
491
492     case INTF_MSG_INTF:                                /* interface messages */
493         asprintf( &psz_msg, "%s", p_msg->psz_msg );
494         break;
495
496     case INTF_MSG_DBG:                                     /* debug messages */
497         mstrtime( psz_date, p_msg->date );
498         asprintf( &psz_msg, "(%s) " INTF_MSG_DBG_FORMAT "%s",
499                   psz_date, p_msg->psz_file, p_msg->psz_function, p_msg->i_line,
500                   p_msg->psz_msg );
501         break;
502     }
503
504     /* Check if formatting function suceeded */
505     if( psz_msg == NULL )
506     {
507         fprintf( stderr, "error: can not format message (%s): %s\n",
508                  strerror( errno ), p_msg->psz_msg );
509         return;
510     }
511
512     /*
513      * Print messages
514      */
515     switch( p_msg->i_type )
516     {
517     case INTF_MSG_STD:                                  /* standard messages */
518         fprintf( stdout, psz_msg );
519         break;
520     case INTF_MSG_ERR:                                     /* error messages */
521 #ifndef DEBUG_LOG_ONLY
522     case INTF_MSG_DBG:                                 /* debugging messages */
523 #endif
524         fprintf( stderr, psz_msg );
525         break;
526     case INTF_MSG_INTF:                                /* interface messages */
527         intf_ConsolePrint( p_main->p_intf->p_console, psz_msg );
528         break;
529     }
530
531 #ifdef DEBUG_LOG
532     /* Append all messages to log file */
533     if( p_main->p_msg->i_log_file >= 0 )
534     {
535         write( p_main->p_msg->i_log_file, psz_msg, strlen( psz_msg ) );
536     }
537 #endif
538
539     /* Free formatted message */
540     free( psz_msg );
541 }
542
543 #else
544
545 static void PrintMsg( intf_msg_item_t *p_msg )
546 {
547     /*
548      * Print messages on screen
549      */
550     switch( p_msg->i_type )
551     {
552     case INTF_MSG_STD:                                  /* standard messages */
553     case INTF_MSG_DBG:                                     /* debug messages */
554         fprintf( stdout, p_msg->psz_msg );
555         break;
556     case INTF_MSG_ERR:                                     /* error messages */
557         fprintf( stderr, p_msg->psz_msg );
558         break;
559     case INTF_MSG_INTF:                                /* interface messages */
560         intf_ConsolePrint( p_main->p_intf->p_console, p_msg->psz_msg );
561         break;
562     }
563 }
564
565 #endif