events.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:13k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * events.c: events interface
  3.  * This library provides an interface to the send and receive events.
  4.  * It is more lightweight than variable based callback.
  5.  *****************************************************************************
  6.  * Copyright (C) 1998-2005 the VideoLAN team
  7.  * $Id: 9d7602a0b8f52db799f8cd8976b06a62c5f177ee $
  8.  *
  9.  * Authors: Pierre d'Herbemont <pdherbemont # videolan.org >
  10.  *
  11.  * This program is free software; you can redistribute it and/or modify
  12.  * it under the terms of the GNU General Public License as published by
  13.  * the Free Software Foundation; either version 2 of the License, or
  14.  * (at your option) any later version.
  15.  *
  16.  * This program is distributed in the hope that it will be useful,
  17.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.  * GNU General Public License for more details.
  20.  *
  21.  * You should have received a copy of the GNU General Public License
  22.  * along with this program; if not, write to the Free Software
  23.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  24.  *****************************************************************************/
  25. /*****************************************************************************
  26.  * Preamble
  27.  *****************************************************************************/
  28. #ifdef HAVE_CONFIG_H
  29. # include "config.h"
  30. #endif
  31. #include <vlc_common.h>
  32. #include <assert.h>
  33. #include <vlc_events.h>
  34. #include <vlc_arrays.h>
  35. /*****************************************************************************
  36.  * Documentation : Read vlc_events.h
  37.  *****************************************************************************/
  38. //#define DEBUG_EVENT
  39. /*****************************************************************************
  40.  *  Private types.
  41.  *****************************************************************************/
  42. typedef struct vlc_event_listener_t
  43. {
  44.     void *               p_user_data;
  45.     vlc_event_callback_t pf_callback;
  46. #ifdef DEBUG_EVENT
  47.     char *               psz_debug_name;
  48. #endif
  49. } vlc_event_listener_t;
  50. typedef struct vlc_event_listeners_group_t
  51. {
  52.     vlc_event_type_t    event_type;
  53.     DECL_ARRAY(struct vlc_event_listener_t *) listeners;
  54.    /* Used in vlc_event_send() to make sure to behave
  55.       Correctly when vlc_event_detach was called during
  56.       a callback */
  57.     bool          b_sublistener_removed;
  58.                                          
  59. } vlc_event_listeners_group_t;
  60. #ifdef DEBUG_EVENT
  61. static const char ppsz_event_type_to_name[][40] =
  62. {
  63.     [vlc_InputStateChanged]             = "vlc_InputStateChanged",
  64.     [vlc_InputSelectedStreamChanged]    = "vlc_InputSelectedStreamChanged",
  65.     [vlc_InputItemMetaChanged]          = "vlc_InputItemMetaChanged",
  66.     [vlc_InputItemSubItemAdded]         = "vlc_InputItemSubItemAdded",
  67.     [vlc_InputItemDurationChanged]      = "vlc_InputItemDurationChanged",
  68.     [vlc_InputItemPreparsedChanged]     = "vlc_InputItemPreparsedChanged",
  69.     [vlc_InputItemNameChanged]          = "vlc_InputItemNameChanged",
  70.     [vlc_InputItemInfoChanged]          = "vlc_InputItemInfoChanged",
  71.     [vlc_InputItemErrorWhenReadingChanged] = "vlc_InputItemErrorWhenReadingChanged",
  72.     [vlc_ServicesDiscoveryItemAdded]    = "vlc_ServicesDiscoveryItemAdded",
  73.     [vlc_ServicesDiscoveryItemRemoved]  = "vlc_ServicesDiscoveryItemRemoved",
  74.     [vlc_ServicesDiscoveryStarted]      = "vlc_ServicesDiscoveryStarted",
  75.     [vlc_ServicesDiscoveryEnded]        = "vlc_ServicesDiscoveryEnded"
  76. };
  77. #endif
  78. static bool
  79. listeners_are_equal( vlc_event_listener_t * listener1,
  80.                      vlc_event_listener_t * listener2 )
  81. {
  82.     return listener1->pf_callback == listener2->pf_callback &&
  83.            listener1->p_user_data == listener2->p_user_data;
  84. }
  85. static bool
  86. group_contains_listener( vlc_event_listeners_group_t * group,
  87.                          vlc_event_listener_t * searched_listener )
  88. {
  89.     vlc_event_listener_t * listener;
  90.     FOREACH_ARRAY( listener, group->listeners )
  91.         if( listeners_are_equal(searched_listener, listener) )
  92.             return true;
  93.     FOREACH_END()
  94.     return false;
  95. }
  96. /*****************************************************************************
  97.  *
  98.  *****************************************************************************/
  99. /**
  100.  * Initialize event manager object
  101.  * p_obj is the object that contains the event manager. But not
  102.  * necessarily a vlc_object_t (an input_item_t is not a vlc_object_t
  103.  * for instance).
  104.  * p_parent_obj gives a libvlc instance
  105.  */
  106. int __vlc_event_manager_init( vlc_event_manager_t * p_em, void * p_obj,
  107.                               vlc_object_t * p_parent_obj )
  108. {
  109.     p_em->p_obj = p_obj;
  110.     p_em->p_parent_object = p_parent_obj;
  111.     vlc_mutex_init( &p_em->object_lock );
  112.     /* We need a recursive lock here, because we need to be able
  113.      * to call libvlc_event_detach even if vlc_event_send is in
  114.      * the call frame.
  115.      * This ensures that after libvlc_event_detach, the callback
  116.      * will never gets triggered.
  117.      * */
  118.     vlc_mutex_init_recursive( &p_em->event_sending_lock );
  119.     ARRAY_INIT( p_em->listeners_groups );
  120.     return VLC_SUCCESS;
  121. }
  122. /**
  123.  * Destroy the event manager
  124.  */
  125. void vlc_event_manager_fini( vlc_event_manager_t * p_em )
  126. {
  127.     struct vlc_event_listeners_group_t * listeners_group;
  128.     struct vlc_event_listener_t * listener;
  129.     vlc_mutex_destroy( &p_em->object_lock );
  130.     vlc_mutex_destroy( &p_em->event_sending_lock );
  131.     FOREACH_ARRAY( listeners_group, p_em->listeners_groups )
  132.         FOREACH_ARRAY( listener, listeners_group->listeners )
  133.             free( listener );
  134.         FOREACH_END()
  135.         ARRAY_RESET( listeners_group->listeners );
  136.         free( listeners_group );
  137.     FOREACH_END()
  138.     ARRAY_RESET( p_em->listeners_groups );
  139. }
  140. /**
  141.  * Destroy the event manager
  142.  */
  143. int vlc_event_manager_register_event_type(
  144.         vlc_event_manager_t * p_em,
  145.         vlc_event_type_t event_type )
  146. {
  147.     vlc_event_listeners_group_t * listeners_group;
  148.     listeners_group = malloc(sizeof(vlc_event_listeners_group_t));
  149.     if( !listeners_group )
  150.         return VLC_ENOMEM;
  151.     listeners_group->event_type = event_type;
  152.     ARRAY_INIT( listeners_group->listeners );
  153.  
  154.     vlc_mutex_lock( &p_em->object_lock );
  155.     ARRAY_APPEND( p_em->listeners_groups, listeners_group );
  156.     vlc_mutex_unlock( &p_em->object_lock );
  157.     return VLC_SUCCESS;
  158. }
  159. /**
  160.  * Send an event to the listener attached to this p_em.
  161.  */
  162. void vlc_event_send( vlc_event_manager_t * p_em,
  163.                      vlc_event_t * p_event )
  164. {
  165.     vlc_event_listeners_group_t * listeners_group = NULL;
  166.     vlc_event_listener_t * listener;
  167.     vlc_event_listener_t * array_of_cached_listeners = NULL;
  168.     vlc_event_listener_t * cached_listener;
  169.     int i, i_cached_listeners = 0;
  170.     /* Fill event with the sending object now */
  171.     p_event->p_obj = p_em->p_obj;
  172.     vlc_mutex_lock( &p_em->object_lock );
  173.     FOREACH_ARRAY( listeners_group, p_em->listeners_groups )
  174.         if( listeners_group->event_type == p_event->type )
  175.         {
  176.             if( listeners_group->listeners.i_size <= 0 )
  177.                 break;
  178.             /* Save the function to call */
  179.             i_cached_listeners = listeners_group->listeners.i_size;
  180.             array_of_cached_listeners = malloc(
  181.                     sizeof(vlc_event_listener_t)*i_cached_listeners );
  182.             if( !array_of_cached_listeners )
  183.             {
  184.                 vlc_mutex_unlock( &p_em->object_lock );
  185.                 return;
  186.             }
  187.             cached_listener = array_of_cached_listeners;
  188.             FOREACH_ARRAY( listener, listeners_group->listeners )
  189.                 memcpy( cached_listener, listener, sizeof(vlc_event_listener_t));
  190. #ifdef DEBUG_EVENT
  191.                 cached_listener->psz_debug_name = strdup(cached_listener->psz_debug_name);
  192. #endif
  193.                 cached_listener++;
  194.             FOREACH_END()
  195.             break;
  196.         }
  197.     FOREACH_END()
  198.     vlc_mutex_unlock( &p_em->object_lock );
  199.  
  200.     /* Call the function attached */
  201.     cached_listener = array_of_cached_listeners;
  202.     if( !listeners_group || !array_of_cached_listeners )
  203.     {
  204.         free( array_of_cached_listeners );
  205.         return;
  206.     }
  207.     vlc_mutex_lock( &p_em->event_sending_lock ) ;
  208.     /* Track item removed from *this* thread, with a simple flag */
  209.     listeners_group->b_sublistener_removed = false;
  210.     for( i = 0; i < i_cached_listeners; i++ )
  211.     {
  212. #ifdef DEBUG_EVENT
  213.         msg_Dbg( p_em->p_parent_object,
  214.                     "Calling '%s' with a '%s' event (data %p)",
  215.                     cached_listener->psz_debug_name,
  216.                     ppsz_event_type_to_name[p_event->type],
  217.                     cached_listener->p_user_data );
  218.         free(cached_listener->psz_debug_name);
  219. #endif
  220.         /* No need to lock on listeners_group, a listener group can't be removed */
  221.         if( listeners_group->b_sublistener_removed )
  222.         {
  223.             /* If a callback was removed, this gets called */
  224.             bool valid_listener;
  225.             vlc_mutex_lock( &p_em->object_lock );
  226.             valid_listener = group_contains_listener( listeners_group, cached_listener );
  227.             vlc_mutex_unlock( &p_em->object_lock );
  228.             if( !valid_listener )
  229.             {
  230. #ifdef DEBUG_EVENT
  231.                 msg_Dbg( p_em->p_parent_object, "Callback was removed during execution" );
  232. #endif
  233.                 cached_listener++;
  234.                 continue;
  235.             }
  236.         }
  237.         cached_listener->pf_callback( p_event, cached_listener->p_user_data );
  238.         cached_listener++;
  239.     }
  240.     vlc_mutex_unlock( &p_em->event_sending_lock );
  241.     free( array_of_cached_listeners );
  242. }
  243. /**
  244.  * Add a callback for an event.
  245.  */
  246. int __vlc_event_attach( vlc_event_manager_t * p_em,
  247.                         vlc_event_type_t event_type,
  248.                         vlc_event_callback_t pf_callback,
  249.                         void *p_user_data,
  250.                         const char * psz_debug_name )
  251. {
  252.     vlc_event_listeners_group_t * listeners_group;
  253.     vlc_event_listener_t * listener;
  254.     listener = malloc(sizeof(vlc_event_listener_t));
  255.     if( !listener )
  256.         return VLC_ENOMEM;
  257.  
  258.     listener->p_user_data = p_user_data;
  259.     listener->pf_callback = pf_callback;
  260. #ifdef DEBUG_EVENT
  261.     listener->psz_debug_name = strdup( psz_debug_name );
  262. #else
  263.     (void)psz_debug_name;
  264. #endif
  265.     vlc_mutex_lock( &p_em->object_lock );
  266.     FOREACH_ARRAY( listeners_group, p_em->listeners_groups )
  267.         if( listeners_group->event_type == event_type )
  268.         {
  269.             ARRAY_APPEND( listeners_group->listeners, listener );
  270. #ifdef DEBUG_EVENT
  271.                 msg_Dbg( p_em->p_parent_object,
  272.                     "Listening to '%s' event with '%s' (data %p)",
  273.                     ppsz_event_type_to_name[event_type],
  274.                     listener->psz_debug_name,
  275.                     listener->p_user_data );
  276. #endif
  277.             vlc_mutex_unlock( &p_em->object_lock );
  278.             return VLC_SUCCESS;
  279.         }
  280.     FOREACH_END()
  281.     vlc_mutex_unlock( &p_em->object_lock );
  282.     msg_Err( p_em->p_parent_object, "cannot attach to an object event" );
  283.     free(listener);
  284.     return VLC_EGENERIC;
  285. }
  286. /**
  287.  * Remove a callback for an event.
  288.  */
  289. int vlc_event_detach( vlc_event_manager_t *p_em,
  290.                       vlc_event_type_t event_type,
  291.                       vlc_event_callback_t pf_callback,
  292.                       void *p_user_data )
  293. {
  294.     vlc_event_listeners_group_t * listeners_group;
  295.     struct vlc_event_listener_t * listener;
  296.     vlc_mutex_lock( &p_em->object_lock );
  297.     vlc_mutex_lock( &p_em->event_sending_lock );
  298.     FOREACH_ARRAY( listeners_group, p_em->listeners_groups )
  299.         if( listeners_group->event_type == event_type )
  300.         {
  301.             FOREACH_ARRAY( listener, listeners_group->listeners )
  302.                 if( listener->pf_callback == pf_callback &&
  303.                     listener->p_user_data == p_user_data )
  304.                 {
  305.                     /* Tell vlc_event_send, we did remove an item from that group,
  306.                        in case vlc_event_send is in our caller stack  */
  307.                     listeners_group->b_sublistener_removed = true;
  308.                     /* that's our listener */
  309.                     ARRAY_REMOVE( listeners_group->listeners,
  310.                         fe_idx /* This comes from the macro (and that's why
  311.                                   I hate macro) */ );
  312. #ifdef DEBUG_EVENT
  313.                     msg_Dbg( p_em->p_parent_object,
  314.                         "Detaching '%s' from '%s' event (data %p)",
  315.                         listener->psz_debug_name,
  316.                         ppsz_event_type_to_name[event_type],
  317.                         listener->p_user_data );
  318.                     free( listener->psz_debug_name );
  319. #endif
  320.                     free( listener );
  321.                     vlc_mutex_unlock( &p_em->event_sending_lock );
  322.                     vlc_mutex_unlock( &p_em->object_lock );
  323.                     return VLC_SUCCESS;
  324.                 }
  325.             FOREACH_END()
  326.         }
  327.     FOREACH_END()
  328.     vlc_mutex_unlock( &p_em->event_sending_lock );
  329.     vlc_mutex_unlock( &p_em->object_lock );
  330.     msg_Warn( p_em->p_parent_object, "cannot detach from an object event" );
  331.     return VLC_EGENERIC;
  332. }