]> git.sesse.net Git - vlc/blobdiff - bindings/cil/src/marshal.cs
Simpler and safer event callbacks handling
[vlc] / bindings / cil / src / marshal.cs
index dfb8180e42c59278a6c42717fdadfb920d863049..3a427f8f25212978181480e74fc1e38276d9a57f 100644 (file)
@@ -22,6 +22,8 @@
  **********************************************************************/
 
 using System;
+using System.Collections;
+using System.Collections.Generic;
 using System.Runtime.InteropServices;
 
 namespace VideoLAN.LibVLC
@@ -63,14 +65,15 @@ namespace VideoLAN.LibVLC
             Destroy ();
             return true;
         }
-
     };
 
     /**
      * @brief BaseObject: generic wrapper around a safe LibVLC handle.
      * @ingroup Internals
-     * This is the baseline for all managed LibVLC objects which wrap
-     * an unmanaged LibVLC pointer, and provides exception handling.
+     *
+     * This is the baseline for all managed LibVLC objects. It wraps:
+     *  - an unmanaged LibVLC pointer,
+     *  - a native exception structure.
      */
     public class BaseObject : IDisposable
     {
@@ -118,4 +121,101 @@ namespace VideoLAN.LibVLC
             handle = null;
         }
     };
+
+    internal class EventManagerHandle : NonNullHandle
+    {
+        protected override void Destroy ()
+        {
+        }
+    };
+
+
+    /**
+     * @brief EventingObject: wrapper around an eventing LibVLC handle.
+     * @ingroup Internals
+     *
+     * This is the base class for all managed LibVLC objects which do have an
+     * event manager.
+     */
+    public abstract class EventingObject : BaseObject
+    {
+        private Dictionary<Delegate, IntPtr> events;
+        /**< references to our unmanaged function pointers */
+
+        internal EventingObject () : base ()
+        {
+            events = new Dictionary<Delegate, IntPtr> ();
+        }
+
+        /**
+         * Releases unmanaged resources associated with the object.
+         * @param disposing true if the disposing the object explicitly,
+         *                  false if finalizing the object inside the GC.
+         */
+        protected override void Dispose (bool disposing)
+        {
+            events = null;
+            base.Dispose (disposing);
+        }
+
+        /**
+         * @return the unmanaged event manager for this object
+         */
+        internal abstract EventManagerHandle GetManager ();
+
+        /**
+         * Registers an event handler.
+         * @param type event type to register to
+         * @param callback callback to invoke when the event occurs
+         *
+         * @note
+         * For simplicity, we require distinct callbacks for each event type.
+         * This is hardly an issue since most events have different formats.
+         */
+        internal void Attach (EventType type, Delegate callback)
+        {
+            EventManagerHandle manager;
+            IntPtr cb = Marshal.GetFunctionPointerForDelegate (callback);
+            bool unref = false;
+
+            /* If things go wrong, we will leak the callback thunk... until
+             * this object is destroyed anyway. If we added the thunk _after_
+             * the critical section, the native code could try to jump to a
+             * non-existent address, which is much worse. */
+            events.Add (callback, cb);
+            try
+            {
+                handle.DangerousAddRef (ref unref);
+                manager = GetManager ();
+                LibVLC.EventAttach (manager, type, cb, IntPtr.Zero, ex);
+            }
+            finally
+            {
+                if (unref)
+                    handle.DangerousRelease ();
+            }
+            Raise ();
+        }
+
+        internal void Detach (EventType type, Delegate callback)
+        {
+            EventManagerHandle manager;
+            IntPtr cb = events[callback];
+            bool unref = false;
+
+            try
+            {
+                handle.DangerousAddRef (ref unref);
+                manager = GetManager ();
+                LibVLC.EventDetach (manager, type, cb, IntPtr.Zero, ex);
+            }
+            finally
+            {
+                if (unref)
+                    handle.DangerousRelease ();
+            }
+            Raise ();
+            events.Remove (callback);
+        }
+    };
 };