Low-level support for events attaching/detaching
[vlc.git] / bindings / cil / src / marshal.cs
1 /**
2  * @file marshal.cs
3  * @brief Common LibVLC objects marshalling utilities
4  * @ingroup Internals
5  */
6
7 /**********************************************************************
8  *  Copyright (C) 2007-2009 RĂ©mi Denis-Courmont.                      *
9  *  This program is free software; you can redistribute and/or modify *
10  *  it under the terms of the GNU General Public License as published *
11  *  by the Free Software Foundation; version 2 of the license, or (at *
12  *  your option) any later version.                                   *
13  *                                                                    *
14  *  This program is distributed in the hope that it will be useful,   *
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of    *
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.              *
17  *  See the GNU General Public License for more details.              *
18  *                                                                    *
19  *  You should have received a copy of the GNU General Public License *
20  *  along with this program; if not, you can get it from:             *
21  *  http://www.gnu.org/copyleft/gpl.html                              *
22  **********************************************************************/
23
24 using System;
25 using System.Collections;
26 using System.Collections.Generic;
27 using System.Runtime.InteropServices;
28
29 namespace VideoLAN.LibVLC
30 {
31     /**
32      * @brief NonNullHandle: abstract safe handle class for non-NULL pointers
33      * @ingroup Internals
34      * Microsoft.* namespace has a similar class. However we want to use the
35      * System.* namespace only.
36      */
37     internal abstract class NonNullHandle : SafeHandle
38     {
39         protected NonNullHandle ()
40             : base (IntPtr.Zero, true)
41         {
42         }
43
44         /**
45          * System.Runtime.InteropServices.SafeHandle::IsInvalid.
46          */
47         public override bool IsInvalid
48         {
49             get
50             {
51                 return handle == IntPtr.Zero;
52             }
53         }
54
55         /**
56          * Destroys an handle. Cannot fail.
57          */
58         protected abstract void Destroy ();
59
60         /**
61          * System.Runtime.InteropServices.SafeHandle::ReleaseHandle.
62          */
63         protected override bool ReleaseHandle ()
64         {
65             Destroy ();
66             return true;
67         }
68     };
69
70     /**
71      * @brief BaseObject: generic wrapper around a safe LibVLC handle.
72      * @ingroup Internals
73      *
74      * This is the baseline for all managed LibVLC objects. It wraps:
75      *  - an unmanaged LibVLC pointer,
76      *  - a native exception structure.
77      */
78     public class BaseObject : IDisposable
79     {
80         internal NativeException ex; /**< buffer for LibVLC exceptions */
81         internal SafeHandle handle; /**< wrapped safe handle */
82
83         internal BaseObject ()
84         {
85             ex = new NativeException ();
86             this.handle = null;
87         }
88
89         /**
90          * Checks if the LibVLC run-time raised an exception
91          * If so, raises a CIL exception.
92          */
93         internal void Raise ()
94         {
95             ex.Raise ();
96         }
97
98         /**
99          * IDisposable::Dispose.
100          */
101         public void Dispose ()
102         {
103             Dispose (true);
104             GC.SuppressFinalize (this);
105         }
106
107         /**
108          * Releases unmanaged resources associated with the object.
109          * @param disposing true if the disposing the object explicitly,
110          *                  false if finalizing the object inside the GC.
111          */
112         protected virtual void Dispose (bool disposing)
113         {
114             if (disposing)
115             {
116                 ex.Dispose ();
117                 if (handle != null)
118                     handle.Close ();
119             }
120             ex = null;
121             handle = null;
122         }
123     };
124
125     internal class EventManagerHandle : NonNullHandle
126     {
127         protected override void Destroy ()
128         {
129         }
130     };
131
132
133     /**
134      * @brief EventingObject: wrapper around an eventing LibVLC handle.
135      * @ingroup Internals
136      *
137      * This is the base class for all managed LibVLC objects which do have an
138      * event manager.
139      */
140     public abstract class EventingObject : BaseObject
141     {
142         /**
143          * @brief Managed to unmanaged event handler mapping
144          * @ingroup Internals
145          *
146          * The CLR cannot do reference counting for unmanaged callbacks.
147          * We keep track of handled events here instead.
148          */
149         private class Event
150         {
151             public EventCallback managed;
152             public IntPtr        unmanaged;
153
154             public Event (EventCallback managed, IntPtr unmanaged)
155             {
156                 this.managed = managed;
157                 this.unmanaged = unmanaged;
158             }
159         };
160         private Dictionary<EventType, Event> events;
161         /**< references to our unmanaged function pointers */
162
163         internal EventingObject () : base ()
164         {
165             events = new Dictionary<EventType, Event> ();
166         }
167
168         /**
169          * Releases unmanaged resources associated with the object.
170          * @param disposing true if the disposing the object explicitly,
171          *                  false if finalizing the object inside the GC.
172          */
173         protected override void Dispose (bool disposing)
174         {
175             events = null;
176             base.Dispose (disposing);
177         }
178
179         /**
180          * @return the unmanaged event manager for this object
181          */
182         internal abstract EventManagerHandle GetManager ();
183
184         /**
185          * Registers an event handler.
186          * @param type event type to register to
187          * @param callback callback to invoke when the event occurs
188          *
189          * @note
190          * For simplicity, we only allow one handler per type.
191          * Multicasting can be implemented higher up with managed code.
192          */
193         internal void Attach (EventType type, EventCallback callback)
194         {
195             EventManagerHandle manager;
196             IntPtr cb = Marshal.GetFunctionPointerForDelegate (callback);
197             Event ev = new Event (callback, cb);
198             bool unref = false;
199
200             if (events.ContainsKey (type))
201                 throw new ArgumentException ("Duplicate event");
202
203             try
204             {
205                 handle.DangerousAddRef (ref unref);
206                 manager = GetManager ();
207                 LibVLC.EventAttach (manager, type, cb, IntPtr.Zero, ex);
208             }
209             finally
210             {
211                 if (unref)
212                     handle.DangerousRelease ();
213             }
214             Raise ();
215             events.Add (type, ev);
216         }
217
218         private void Detach (EventType type, IntPtr callback)
219         {
220             EventManagerHandle manager;
221             bool unref = false;
222
223             try
224             {
225                 handle.DangerousAddRef (ref unref);
226                 manager = GetManager ();
227                 LibVLC.EventDetach (manager, type, callback, IntPtr.Zero, ex);
228             }
229             finally
230             {
231                 if (unref)
232                     handle.DangerousRelease ();
233             }
234             Raise ();
235             events.Remove (type);
236         }
237
238         internal void Detach (EventType type)
239         {
240             Detach(type, events[type].unmanaged);
241         }
242     };
243 };