event.c 14 KB
Newer Older
Filippo Carone's avatar
Filippo Carone committed
1
/*****************************************************************************
2
 * event.c: New libvlc event control API
Filippo Carone's avatar
Filippo Carone committed
3
 *****************************************************************************
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2007-2010 VLC authors and VideoLAN
Filippo Carone's avatar
Filippo Carone committed
5 6 7
 * $Id $
 *
 * Authors: Filippo Carone <filippo@carone.org>
8
 *          Pierre d'Herbemont <pdherbemont # videolan.org>
Filippo Carone's avatar
Filippo Carone committed
9
 *
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
10 11 12
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
Filippo Carone's avatar
Filippo Carone committed
13 14 15 16
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
17 18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
Filippo Carone's avatar
Filippo Carone committed
19
 *
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
20 21 22
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
Filippo Carone's avatar
Filippo Carone committed
23 24
 *****************************************************************************/

25 26 27 28
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

29
#define  LIBVLC_EVENT_TYPES_KEEP_DEFINE
Filippo Carone's avatar
Filippo Carone committed
30
#include <vlc/libvlc.h>
31 32 33

#include "libvlc_internal.h"
#include "event_internal.h"
34
#include <assert.h>
35
#include <errno.h>
36 37 38 39 40 41 42

typedef struct libvlc_event_listeners_group_t
{
    libvlc_event_type_t event_type;
    vlc_array_t listeners;
    bool b_sublistener_removed;
} libvlc_event_listeners_group_t;
Filippo Carone's avatar
Filippo Carone committed
43

44 45 46 47
/*
 * Private functions
 */

48
static bool
49 50 51 52 53 54 55
group_contains_listener( libvlc_event_listeners_group_t * group,
                         libvlc_event_listener_t * searched_listener )
{
    int i;
    for( i = 0; i < vlc_array_count(&group->listeners); i++ )
    {
        if( listeners_are_equal(searched_listener, vlc_array_item_at_index(&group->listeners, i)) )
56
            return true;
57
    }
58
    return false;
59 60
}

61 62 63
/*
 * Internal libvlc functions
 */
Filippo Carone's avatar
Filippo Carone committed
64

65
/**************************************************************************
66
 *       libvlc_event_manager_new (internal) :
67
 *
68
 * Init an object's event manager.
69
 **************************************************************************/
70
libvlc_event_manager_t *
71
libvlc_event_manager_new( void * p_obj )
72
{
73
    libvlc_event_manager_t * p_em;
74

75 76 77
    p_em = malloc(sizeof( libvlc_event_manager_t ));
    if( !p_em )
    {
78
        libvlc_printerr( "Not enough memory" );
79 80
        return NULL;
    }
81

82 83
    p_em->p_obj = p_obj;

84
    vlc_array_init( &p_em->listeners_groups );
85
    vlc_mutex_init( &p_em->object_lock );
86
    vlc_mutex_init_recursive( &p_em->event_sending_lock );
87
    return p_em;
88 89
}

90
/**************************************************************************
91
 *       libvlc_event_manager_release (internal) :
92
 *
93
 * Release an object's event manager.
94
 **************************************************************************/
95
void libvlc_event_manager_release( libvlc_event_manager_t * p_em )
96
{
97 98
    libvlc_event_listeners_group_t * p_lg;
    int i,j ;
99

100 101
    vlc_mutex_destroy( &p_em->event_sending_lock );
    vlc_mutex_destroy( &p_em->object_lock );
102

103 104 105 106 107
    for( i = 0; i < vlc_array_count(&p_em->listeners_groups); i++)
    {
        p_lg = vlc_array_item_at_index( &p_em->listeners_groups, i );

        for( j = 0; j < vlc_array_count(&p_lg->listeners); j++)
Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
108
            free( vlc_array_item_at_index( &p_lg->listeners, j ) );
109

110 111 112 113
        vlc_array_clear( &p_lg->listeners );
        free( p_lg );
    }
    vlc_array_clear( &p_em->listeners_groups );
114
    free( p_em );
115 116
}

117
/**************************************************************************
118
 *       libvlc_event_manager_register_event_type (internal) :
119
 *
120
 * Init an object's event manager.
121
 **************************************************************************/
122
void libvlc_event_manager_register_event_type(
123
        libvlc_event_manager_t * p_em,
124
        libvlc_event_type_t event_type )
125
{
126
    libvlc_event_listeners_group_t * listeners_group;
127
    listeners_group = xmalloc(sizeof(libvlc_event_listeners_group_t));
128
    listeners_group->event_type = event_type;
129
    vlc_array_init( &listeners_group->listeners );
130

131
    vlc_mutex_lock( &p_em->object_lock );
132
    vlc_array_append( &p_em->listeners_groups, listeners_group );
133
    vlc_mutex_unlock( &p_em->object_lock );
134
}
135

136
/**************************************************************************
137
 *       libvlc_event_send (internal) :
138
 *
139
 * Send a callback.
140
 **************************************************************************/
141 142
void libvlc_event_send( libvlc_event_manager_t * p_em,
                        libvlc_event_t * p_event )
143
{
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
144
    libvlc_event_listeners_group_t * listeners_group = NULL;
145
    libvlc_event_listener_t * listener_cached;
146
    libvlc_event_listener_t * listener;
147 148 149
    libvlc_event_listener_t * array_listeners_cached = NULL;
    int i, i_cached_listeners = 0;

150 151 152
    /* Fill event with the sending object now */
    p_event->p_obj = p_em->p_obj;

Rafaël Carré's avatar
Rafaël Carré committed
153
    vlc_mutex_lock( &p_em->event_sending_lock );
154
    vlc_mutex_lock( &p_em->object_lock );
155 156 157
    for( i = 0; i < vlc_array_count(&p_em->listeners_groups); i++)
    {
        listeners_group = vlc_array_item_at_index(&p_em->listeners_groups, i);
158 159
        if( listeners_group->event_type == p_event->type )
        {
160
            if( vlc_array_count( &listeners_group->listeners ) <= 0 )
161 162
                break;

163 164
            /* Cache a copy of the listener to avoid locking issues,
             * and allow that edition of listeners during callbacks will garantee immediate effect. */
165
            i_cached_listeners = vlc_array_count(&listeners_group->listeners);
166 167 168
            array_listeners_cached = malloc(sizeof(libvlc_event_listener_t)*(i_cached_listeners));
            if( !array_listeners_cached )
            {
169
                vlc_mutex_unlock( &p_em->object_lock );
Rafaël Carré's avatar
Rafaël Carré committed
170
                vlc_mutex_unlock( &p_em->event_sending_lock );
171 172
                fprintf(stderr, "Can't alloc memory in libvlc_event_send" );
                return;
173 174 175
            }

            listener_cached = array_listeners_cached;
176 177 178 179
            for( i = 0; i < vlc_array_count(&listeners_group->listeners); i++)
            {
                listener = vlc_array_item_at_index(&listeners_group->listeners, i);
                memcpy( listener_cached, listener, sizeof(libvlc_event_listener_t) );
180
                listener_cached++;
181
            }
182 183
            break;
        }
184
    }
185

186 187 188
    if( !listeners_group )
    {
        free( array_listeners_cached );
189
        vlc_mutex_unlock( &p_em->object_lock );
Rafaël Carré's avatar
Rafaël Carré committed
190
        vlc_mutex_unlock( &p_em->event_sending_lock );
191 192 193
        return;
    }

Rafaël Carré's avatar
Rafaël Carré committed
194 195 196 197 198
    /* Track item removed from *this* thread, with a simple flag. Indeed
     * event_sending_lock is a recursive lock. This has the advantage of
     * allowing to remove an event listener from within a callback */
    listeners_group->b_sublistener_removed = false;

199 200 201 202 203
    vlc_mutex_unlock( &p_em->object_lock );

    listener_cached = array_listeners_cached;
    for( i = 0; i < i_cached_listeners; i++ )
    {
204 205 206
        /* The listener wants to block the emitter during event callback */
        listener_cached->pf_callback( p_event, listener_cached->p_user_data );
        listener_cached++;
207

208 209 210 211 212 213 214 215
        if( listeners_group->b_sublistener_removed )
        {
            /* If a callback was removed, this gets called */
            bool valid_listener;
            vlc_mutex_lock( &p_em->object_lock );
            valid_listener = group_contains_listener( listeners_group, listener_cached );
            vlc_mutex_unlock( &p_em->object_lock );
            if( !valid_listener )
216
            {
217 218
                listener_cached++;
                continue;
219
            }
220
        }
221
    }
222
    vlc_mutex_unlock( &p_em->event_sending_lock );
223

224
    free( array_listeners_cached );
225 226
}

227 228 229 230
/*
 * Public libvlc functions
 */

231 232 233 234 235 236 237 238 239 240 241 242
#define DEF( a ) { libvlc_##a, #a, },

typedef struct
{
    int type;
    const char name[40];
} event_name_t;

static const event_name_t event_list[] = {
    DEF(MediaMetaChanged)
    DEF(MediaSubItemAdded)
    DEF(MediaDurationChanged)
243
    DEF(MediaParsedChanged)
244 245
    DEF(MediaFreed)
    DEF(MediaStateChanged)
246
    DEF(MediaSubItemTreeAdded)
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265

    DEF(MediaPlayerMediaChanged)
    DEF(MediaPlayerNothingSpecial)
    DEF(MediaPlayerOpening)
    DEF(MediaPlayerBuffering)
    DEF(MediaPlayerPlaying)
    DEF(MediaPlayerPaused)
    DEF(MediaPlayerStopped)
    DEF(MediaPlayerForward)
    DEF(MediaPlayerBackward)
    DEF(MediaPlayerEndReached)
    DEF(MediaPlayerEncounteredError)
    DEF(MediaPlayerTimeChanged)
    DEF(MediaPlayerPositionChanged)
    DEF(MediaPlayerSeekableChanged)
    DEF(MediaPlayerPausableChanged)
    DEF(MediaPlayerTitleChanged)
    DEF(MediaPlayerSnapshotTaken)
    DEF(MediaPlayerLengthChanged)
266
    DEF(MediaPlayerVout)
267
    DEF(MediaPlayerScrambledChanged)
268 269 270
    DEF(MediaPlayerESAdded)
    DEF(MediaPlayerESDeleted)
    DEF(MediaPlayerESSelected)
271 272
    DEF(MediaPlayerCorked)
    DEF(MediaPlayerUncorked)
273 274
    DEF(MediaPlayerMuted)
    DEF(MediaPlayerUnmuted)
275
    DEF(MediaPlayerAudioVolume)
276
    DEF(MediaPlayerAudioDevice)
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301

    DEF(MediaListItemAdded)
    DEF(MediaListWillAddItem)
    DEF(MediaListItemDeleted)
    DEF(MediaListWillDeleteItem)

    DEF(MediaListViewItemAdded)
    DEF(MediaListViewWillAddItem)
    DEF(MediaListViewItemDeleted)
    DEF(MediaListViewWillDeleteItem)

    DEF(MediaListPlayerPlayed)
    DEF(MediaListPlayerNextItemSet)
    DEF(MediaListPlayerStopped)

    DEF(MediaDiscovererStarted)
    DEF(MediaDiscovererEnded)

    DEF(VlmMediaAdded)
    DEF(VlmMediaRemoved)
    DEF(VlmMediaChanged)
    DEF(VlmMediaInstanceStarted)
    DEF(VlmMediaInstanceStopped)
    DEF(VlmMediaInstanceStatusInit)
    DEF(VlmMediaInstanceStatusOpening)
302 303 304 305
    DEF(VlmMediaInstanceStatusPlaying)
    DEF(VlmMediaInstanceStatusPause)
    DEF(VlmMediaInstanceStatusEnd)
    DEF(VlmMediaInstanceStatusError)
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
};
#undef DEF

static const char unknown_event_name[] = "Unknown Event";

static int evcmp( const void *a, const void *b )
{
    return (*(const int *)a) - ((event_name_t *)b)->type;
}

const char * libvlc_event_type_name( int event_type )
{
    const event_name_t *p;

    p = bsearch( &event_type, event_list,
                 sizeof(event_list)/sizeof(event_list[0]), sizeof(*p),
                 evcmp );
    return p ? p->name : unknown_event_name;
}

326
/**************************************************************************
327
 *       event_attach (internal) :
328 329 330
 *
 * Add a callback for an event.
 **************************************************************************/
331
static
332 333
int event_attach( libvlc_event_manager_t * p_event_manager,
                  libvlc_event_type_t event_type,
334
                  libvlc_callback_t pf_callback, void *p_user_data )
Filippo Carone's avatar
Filippo Carone committed
335
{
336 337
    libvlc_event_listeners_group_t * listeners_group;
    libvlc_event_listener_t * listener;
338
    int i;
339

340
    listener = malloc(sizeof(libvlc_event_listener_t));
341 342
    if( unlikely(listener == NULL) )
        return ENOMEM;
343

344 345 346
    listener->event_type = event_type;
    listener->p_user_data = p_user_data;
    listener->pf_callback = pf_callback;
347

348
    vlc_mutex_lock( &p_event_manager->object_lock );
349 350 351
    for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++ )
    {
        listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
352 353
        if( listeners_group->event_type == listener->event_type )
        {
354
            vlc_array_append( &listeners_group->listeners, listener );
355
            vlc_mutex_unlock( &p_event_manager->object_lock );
356
            return 0;
357
        }
358
    }
359
    vlc_mutex_unlock( &p_event_manager->object_lock );
360

361
    free(listener);
362 363
    fprintf( stderr, "This object event manager doesn't know about '%s' events",
             libvlc_event_type_name(event_type) );
364
    vlc_assert_unreachable();
365
    return -1;
366 367 368 369 370 371 372
}

/**************************************************************************
 *       libvlc_event_attach (public) :
 *
 * Add a callback for an event.
 **************************************************************************/
373
int libvlc_event_attach( libvlc_event_manager_t * p_event_manager,
374 375
                         libvlc_event_type_t event_type,
                         libvlc_callback_t pf_callback,
376
                         void *p_user_data )
377
{
378
    return event_attach(p_event_manager, event_type, pf_callback, p_user_data);
Filippo Carone's avatar
Filippo Carone committed
379 380
}

381
/**************************************************************************
382
 *       libvlc_event_detach (public) :
383 384 385
 *
 * Remove a callback for an event.
 **************************************************************************/
386
void libvlc_event_detach( libvlc_event_manager_t *p_event_manager,
387 388
                                     libvlc_event_type_t event_type,
                                     libvlc_callback_t pf_callback,
389
                                     void *p_user_data )
Filippo Carone's avatar
Filippo Carone committed
390
{
391 392
    libvlc_event_listeners_group_t * listeners_group;
    libvlc_event_listener_t * listener;
393
    int i, j;
394
    bool found = false;
395

396
    vlc_mutex_lock( &p_event_manager->event_sending_lock );
397
    vlc_mutex_lock( &p_event_manager->object_lock );
398 399 400
    for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++)
    {
        listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
401 402
        if( listeners_group->event_type == event_type )
        {
403
            for( j = 0; j < vlc_array_count(&listeners_group->listeners); j++)
404
            {
405
                listener = vlc_array_item_at_index(&listeners_group->listeners, j);
406 407 408 409 410
                if( listener->event_type == event_type &&
                    listener->pf_callback == pf_callback &&
                    listener->p_user_data == p_user_data )
                {
                    /* that's our listener */
411

412 413
                    /* Mark this group as edited so that libvlc_event_send
                     * will recheck what listener to call */
Rafaël Carré's avatar
Rafaël Carré committed
414
                    listeners_group->b_sublistener_removed = true;
415

416
                    free( listener );
417
                    vlc_array_remove( &listeners_group->listeners, j );
418 419
                    found = true;
                    break;
420
                }
421
            }
422
        }
423
    }
424
    vlc_mutex_unlock( &p_event_manager->object_lock );
425
    vlc_mutex_unlock( &p_event_manager->event_sending_lock );
426

427
    assert(found);
Filippo Carone's avatar
Filippo Carone committed
428
}