]> git.sesse.net Git - vlc/blob - src/misc/messages.c
* ./include/*, ./src/*: separated WIN32 #tests and UNDER_CE #tests, because
[vlc] / src / misc / messages.c
1 /*****************************************************************************
2  * messages.c: messages interface
3  * This library provides an interface to the message queue to be used by other
4  * modules, especially intf modules. See config.h for output configuration.
5  *****************************************************************************
6  * Copyright (C) 1998-2002 VideoLAN
7  * $Id: messages.c,v 1.22 2002/11/11 14:39:12 sam Exp $
8  *
9  * Authors: Vincent Seguin <seguin@via.ecp.fr>
10  *          Samuel Hocevar <sam@zoy.org>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #include <stdio.h>                                               /* required */
31 #include <stdarg.h>                                       /* va_list for BSD */
32 #include <stdlib.h>                                              /* malloc() */
33 #include <string.h>                                            /* strerror() */
34
35 #include <vlc/vlc.h>
36
37 #ifdef HAVE_FCNTL_H
38 #   include <fcntl.h>                  /* O_CREAT, O_TRUNC, O_WRONLY, O_SYNC */
39 #endif
40
41 #ifdef HAVE_ERRNO_H
42 #   include <errno.h>                                               /* errno */
43 #endif
44
45 #ifdef HAVE_UNISTD_H
46 #include <unistd.h>                                      /* close(), write() */
47 #endif
48
49 #include "interface.h"
50
51 /*****************************************************************************
52  * Local prototypes
53  *****************************************************************************/
54 static void QueueMsg ( vlc_object_t *, int , const char *,
55                        const char *, va_list );
56 static void FlushMsg ( msg_bank_t * );
57 static void PrintMsg ( vlc_object_t *, msg_item_t * );
58
59 /*****************************************************************************
60  * msg_Create: initialize messages interface
61  *****************************************************************************
62  * This functions has to be called before any call to other msg_* functions.
63  * It set up the locks and the message queue if it is used.
64  *****************************************************************************/
65 void __msg_Create( vlc_object_t *p_this )
66 {
67     /* Message queue initialization */
68     vlc_mutex_init( p_this, &p_this->p_libvlc->msg_bank.lock );
69
70     p_this->p_libvlc->msg_bank.b_configured = VLC_FALSE;
71     p_this->p_libvlc->msg_bank.b_overflow = VLC_FALSE;
72
73     p_this->p_libvlc->msg_bank.i_start = 0;
74     p_this->p_libvlc->msg_bank.i_stop = 0;
75
76     p_this->p_libvlc->msg_bank.i_sub = 0;
77     p_this->p_libvlc->msg_bank.pp_sub = NULL;
78 }
79
80 /*****************************************************************************
81  * msg_Flush: flush the message queue
82  *****************************************************************************/
83 void __msg_Flush( vlc_object_t *p_this )
84 {
85     int i_index;
86
87     vlc_mutex_lock( &p_this->p_libvlc->msg_bank.lock );
88
89     p_this->p_libvlc->msg_bank.b_configured = VLC_TRUE;
90
91     for( i_index = p_this->p_libvlc->msg_bank.i_start;
92          i_index != p_this->p_libvlc->msg_bank.i_stop;
93          i_index = (i_index+1) % VLC_MSG_QSIZE )
94     {
95         PrintMsg( p_this, &p_this->p_libvlc->msg_bank.msg[i_index] );
96     }
97
98     FlushMsg( &p_this->p_libvlc->msg_bank );
99
100     vlc_mutex_unlock( &p_this->p_libvlc->msg_bank.lock );
101 }
102
103 /*****************************************************************************
104  * msg_Destroy: free resources allocated by msg_Create
105  *****************************************************************************
106  * This functions prints all messages remaining in queue, then free all the
107  * resources allocated by msg_Create.
108  * No other messages interface functions should be called after this one.
109  *****************************************************************************/
110 void __msg_Destroy( vlc_object_t *p_this )
111 {
112     if( p_this->p_libvlc->msg_bank.i_sub )
113     {
114         msg_Err( p_this, "stale interface subscribers" );
115     }
116
117     /* Flush the queue */
118     if( !p_this->p_libvlc->msg_bank.b_configured )
119     {
120         msg_Flush( p_this );
121     }
122     else
123     {
124         FlushMsg( &p_this->p_libvlc->msg_bank );
125     }
126
127     /* Destroy lock */
128     vlc_mutex_destroy( &p_this->p_libvlc->msg_bank.lock );
129 }
130
131 /*****************************************************************************
132  * msg_Subscribe: subscribe to the message queue.
133  *****************************************************************************/
134 msg_subscription_t *__msg_Subscribe( vlc_object_t *p_this )
135 {
136     msg_bank_t *p_bank = &p_this->p_libvlc->msg_bank;
137     msg_subscription_t *p_sub = malloc( sizeof( msg_subscription_t ) );
138
139     vlc_mutex_lock( &p_bank->lock );
140
141     /* Add subscription to the list */
142     INSERT_ELEM( p_bank->pp_sub, p_bank->i_sub, p_bank->i_sub, p_sub );
143
144     p_sub->i_start = p_bank->i_start;
145     p_sub->pi_stop = &p_bank->i_stop;
146
147     p_sub->p_msg   = p_bank->msg;
148     p_sub->p_lock  = &p_bank->lock;
149
150     vlc_mutex_unlock( &p_bank->lock );
151
152     return p_sub;
153 }
154
155 /*****************************************************************************
156  * msg_Unsubscribe: unsubscribe from the message queue.
157  *****************************************************************************/
158 void __msg_Unsubscribe( vlc_object_t *p_this, msg_subscription_t *p_sub )
159 {
160     msg_bank_t *p_bank = &p_this->p_libvlc->msg_bank;
161     int i_index;
162
163     vlc_mutex_lock( &p_bank->lock );
164
165     /* Sanity check */
166     if( !p_bank->i_sub )
167     {
168         msg_Err( p_this, "no subscriber in the list" );
169         return;
170     }
171
172     /* Look for the appropriate subscription */
173     for( i_index = 0; i_index < p_bank->i_sub; i_index++ )
174     {
175         if( p_bank->pp_sub[ i_index ] == p_sub )
176         {
177             break;
178         }
179     }
180
181     if( p_bank->pp_sub[ i_index ] != p_sub )
182     {
183         msg_Err( p_this, "subscriber not found" );
184         vlc_mutex_unlock( &p_bank->lock );
185         return;
186     }
187
188     /* Remove this subscription */
189     REMOVE_ELEM( p_bank->pp_sub, p_bank->i_sub, i_index );
190
191     vlc_mutex_unlock( &p_bank->lock );
192 }
193
194 /*****************************************************************************
195  * __msg_*: print a message
196  *****************************************************************************
197  * These functions queue a message for later printing.
198  *****************************************************************************/
199 void __msg_Generic( vlc_object_t *p_this, int i_type, const char *psz_module,
200                     const char *psz_format, ... )
201 {
202     va_list args;
203
204     va_start( args, psz_format );
205     QueueMsg( p_this, i_type, psz_module, psz_format, args );
206     va_end( args );
207 }
208
209 /* Generic functions used when variadic macros are not available. */
210 #define DECLARE_MSG_FN( FN_NAME, FN_TYPE ) \
211     void FN_NAME( void *p_this, const char *psz_format, ... ) \
212     { \
213         va_list args; \
214         va_start( args, psz_format ); \
215         QueueMsg( (vlc_object_t *)p_this, FN_TYPE, "unknown", \
216                   psz_format, args ); \
217         va_end( args ); \
218     } \
219     struct _
220
221 DECLARE_MSG_FN( __msg_Info, VLC_MSG_INFO );
222 DECLARE_MSG_FN( __msg_Err,  VLC_MSG_ERR );
223 DECLARE_MSG_FN( __msg_Warn, VLC_MSG_WARN );
224 DECLARE_MSG_FN( __msg_Dbg,  VLC_MSG_DBG );
225
226 /*****************************************************************************
227  * QueueMsg: add a message to a queue
228  *****************************************************************************
229  * This function provides basic functionnalities to other msg_* functions.
230  * It adds a message to a queue (after having printed all stored messages if it
231  * is full). If the message can't be converted to string in memory, it issues
232  * a warning.
233  *****************************************************************************/
234 static void QueueMsg( vlc_object_t *p_this, int i_type, const char *psz_module,
235                       const char *psz_format, va_list args )
236 {
237     msg_bank_t * p_bank = &p_this->p_libvlc->msg_bank;       /* message bank */
238     char *       psz_str = NULL;                 /* formatted message string */
239     msg_item_t * p_item = NULL;                        /* pointer to message */
240     msg_item_t   item;                    /* message in case of a full queue */
241
242 #if !defined(HAVE_VASPRINTF) || defined(SYS_DARWIN)
243     int          i_size = strlen(psz_format) + INTF_MAX_MSG_SIZE;
244 #endif
245
246     /*
247      * Convert message to string
248      */
249 #if defined(HAVE_VASPRINTF) && !defined(SYS_DARWIN)
250     vasprintf( &psz_str, psz_format, args );
251 #else
252     psz_str = (char*) malloc( i_size * sizeof(char) );
253 #endif
254
255     if( psz_str == NULL )
256     {
257 #ifdef HAVE_ERRNO_H
258         fprintf( stderr, "main warning: can't store message (%s): ",
259                  strerror(errno) );
260 #else
261         fprintf( stderr, "main warning: can't store message: " );
262 #endif
263         vfprintf( stderr, psz_format, args );
264         fprintf( stderr, "\n" );
265         return;
266     }
267
268 #if !defined(HAVE_VASPRINTF) || defined(SYS_DARWIN)
269     vsnprintf( psz_str, i_size, psz_format, args );
270     psz_str[ i_size - 1 ] = 0; /* Just in case */
271 #endif
272
273     /* Put message in queue */
274     vlc_mutex_lock( &p_bank->lock );
275
276     /* Check there is room in the queue for our message */
277     if( p_bank->b_overflow )
278     {
279         FlushMsg( p_bank );
280
281         if( ((p_bank->i_stop - p_bank->i_start + 1) % VLC_MSG_QSIZE) == 0 )
282         {
283             /* Still in overflow mode, print from a dummy item */
284             p_item = &item;
285         }
286         else
287         {
288             /* Pheeew, at last, there is room in the queue! */
289             p_bank->b_overflow = VLC_FALSE;
290         }
291     }
292     else if( ((p_bank->i_stop - p_bank->i_start + 2) % VLC_MSG_QSIZE) == 0 )
293     {
294         FlushMsg( p_bank );
295
296         if( ((p_bank->i_stop - p_bank->i_start + 2) % VLC_MSG_QSIZE) == 0 )
297         {
298             p_bank->b_overflow = VLC_TRUE;
299
300             /* Put the overflow message in the queue */
301             p_item = p_bank->msg + p_bank->i_stop;
302             p_bank->i_stop = (p_bank->i_stop + 1) % VLC_MSG_QSIZE;
303
304             p_item->i_type =        VLC_MSG_ERR;
305             p_item->i_object_id =   p_this->i_object_id;
306             p_item->i_object_type = p_this->i_object_type;
307             p_item->psz_module =    strdup( "message" );
308             p_item->psz_msg =       strdup( "message queue overflowed" );
309
310             PrintMsg( p_this, p_item );
311
312             /* We print from a dummy item */
313             p_item = &item;
314         }
315     }
316
317     if( !p_bank->b_overflow )
318     {
319         /* Put the message in the queue */
320         p_item = p_bank->msg + p_bank->i_stop;
321         p_bank->i_stop = (p_bank->i_stop + 1) % VLC_MSG_QSIZE;
322     }
323
324     /* Fill message information fields */
325     p_item->i_type =        i_type;
326     p_item->i_object_id =   p_this->i_object_id;
327     p_item->i_object_type = p_this->i_object_type;
328     p_item->psz_module =    strdup( psz_module );
329     p_item->psz_msg =       psz_str;
330
331     PrintMsg( p_this, p_item );
332
333     if( p_bank->b_overflow )
334     {
335         free( p_item->psz_module );
336         free( p_item->psz_msg );
337     }
338
339     vlc_mutex_unlock( &p_bank->lock );
340 }
341
342 /* following functions are local */
343
344 /*****************************************************************************
345  * FlushMsg
346  *****************************************************************************
347  * Print all messages remaining in queue. MESSAGE QUEUE MUST BE LOCKED, since
348  * this function does not check the lock.
349  *****************************************************************************/
350 static void FlushMsg ( msg_bank_t *p_bank )
351 {
352     int i_index, i_start, i_stop;
353
354     /* Only flush the queue if it has been properly configured */
355     if( !p_bank->b_configured )
356     {
357         return;
358     }
359
360     /* Get the maximum message index that can be freed */
361     i_stop = p_bank->i_stop;
362
363     /* Check until which value we can free messages */
364     for( i_index = 0; i_index < p_bank->i_sub; i_index++ )
365     {
366         i_start = p_bank->pp_sub[ i_index ]->i_start;
367
368         /* If this subscriber is late, we don't free messages before
369          * his i_start value, otherwise he'll miss messages */
370         if(   ( i_start < i_stop
371                && (p_bank->i_stop <= i_start || i_stop <= p_bank->i_stop) )
372            || ( i_stop < i_start
373                && (i_stop <= p_bank->i_stop && p_bank->i_stop <= i_start) ) )
374         {
375             i_stop = i_start;
376         }
377     }
378
379     /* Free message data */
380     for( i_index = p_bank->i_start;
381          i_index != i_stop;
382          i_index = (i_index+1) % VLC_MSG_QSIZE )
383     {
384         free( p_bank->msg[i_index].psz_msg );
385         free( p_bank->msg[i_index].psz_module );
386     }
387
388     /* Update the new start value */
389     p_bank->i_start = i_index;
390 }
391
392 /*****************************************************************************
393  * PrintMsg: output a message item to stderr
394  *****************************************************************************
395  * Print a message to stderr, with colour formatting if needed.
396  *****************************************************************************/
397 static void PrintMsg ( vlc_object_t * p_this, msg_item_t * p_item )
398 {
399 #   define COL(x)  "\033[" #x ";1m"
400 #   define RED     COL(31)
401 #   define GREEN   COL(32)
402 #   define YELLOW  COL(33)
403 #   define WHITE   COL(37)
404 #   define GRAY    "\033[0m"
405
406     static const char * ppsz_type[4] = { "", " error", " warning", " debug" };
407     static const char *ppsz_color[4] = { WHITE, RED, YELLOW, GRAY };
408     char *psz_object = "private";
409     int i_type = p_item->i_type;
410
411     switch( i_type )
412     {
413         case VLC_MSG_ERR:
414             if( p_this->p_libvlc->i_verbose < 0 ) return;
415             break;
416         case VLC_MSG_INFO:
417             if( p_this->p_libvlc->i_verbose < 0 ) return;
418             break;
419         case VLC_MSG_WARN:
420             if( p_this->p_libvlc->i_verbose < 1 ) return;
421             break;
422         case VLC_MSG_DBG:
423             if( p_this->p_libvlc->i_verbose < 2 ) return;
424             break;
425     }
426
427     switch( p_item->i_object_type )
428     {
429         case VLC_OBJECT_ROOT: psz_object = "root"; break;
430         case VLC_OBJECT_VLC: psz_object = "vlc"; break;
431         case VLC_OBJECT_MODULE: psz_object = "module"; break;
432         case VLC_OBJECT_INTF: psz_object = "interface"; break;
433         case VLC_OBJECT_PLAYLIST: psz_object = "playlist"; break;
434         case VLC_OBJECT_ITEM: psz_object = "item"; break;
435         case VLC_OBJECT_INPUT: psz_object = "input"; break;
436         case VLC_OBJECT_DECODER: psz_object = "decoder"; break;
437         case VLC_OBJECT_VOUT: psz_object = "video output"; break;
438         case VLC_OBJECT_AOUT: psz_object = "audio output"; break;
439         case VLC_OBJECT_SOUT: psz_object = "stream output"; break;
440     }
441
442     /* Send the message to stderr */
443     if( p_this->p_libvlc->b_color )
444     {
445         fprintf( stderr, "[" GREEN "%.8i" GRAY "] %s %s%s: %s%s" GRAY "\n",
446                          p_item->i_object_id, p_item->psz_module, psz_object,
447                          ppsz_type[i_type], ppsz_color[i_type],
448                          p_item->psz_msg );
449     }
450     else
451     {
452         fprintf( stderr, "[%.8i] %s %s%s: %s\n", p_item->i_object_id,
453                          p_item->psz_module, psz_object, ppsz_type[i_type],
454                          p_item->psz_msg );
455     }
456
457 #if defined(WIN32) || defined(UNDER_CE)
458     fflush( stderr );
459 #endif
460 }