MacOSX/Framework: Merge Enrique Osuna's work on the Framework.
[vlc.git] / extras / MacOSX / Framework / Sources / VLCEventManager.m
1 /*****************************************************************************
2  * VLCTime.h: VLC.framework VLCTime implementation
3  *****************************************************************************
4  * Copyright (C) 2007 Pierre d'Herbemont
5  * Copyright (C) 2007 the VideoLAN team
6  * $Id$
7  *
8  * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #import "VLCEventManager.h"
26 #import <pthread.h>
27
28 static VLCEventManager * defaultManager = NULL;
29
30 enum message_type_t
31 {
32     VLCNotification,
33     VLCObjectMethodWithObjectArg
34 };
35
36 struct message {
37     id target;
38     SEL sel;
39     union u
40     {
41         NSString * name;
42         id object;
43     } u;
44     enum message_type_t type;
45 };
46
47 @interface VLCEventManager (PrivateAPI)
48
49 - (void)callDelegateOfObjectAndSendNotificationWithArgs:(NSData*)data;
50 - (void)callObjectMethodWithArgs:(NSData*)data;
51 - (void)callDelegateOfObject:(id) aTarget withDelegateMethod:(SEL)aSelector withNotificationName: (NSString *)aNotificationName;
52 - (pthread_cond_t *)signalData;
53 - (pthread_mutex_t *)queueLock;
54 - (NSMutableArray *)messageQueue;
55
56 @end
57
58 static void * EventDispatcherMainLoop(void * user_data)
59 {
60     VLCEventManager * self = user_data;
61
62     for(;;)
63     {
64         NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
65         struct message * message, * message_newer = NULL;
66         NSData *dataMessage;
67         int i;
68
69         /* Wait for some data */
70         pthread_mutex_lock( [self queueLock] );
71         
72         /* Wait until we have something on the queue */
73         while([[self messageQueue] count] <= 0 )
74                 {
75             pthread_cond_wait( [self signalData], [self queueLock] );
76                 }
77         message = (struct message *)[(NSData *)[[self messageQueue] lastObject] bytes];
78         
79         /* Don't send the same notification twice */
80         if( message->type == VLCNotification )
81         {
82             for( i = 0; i < [[self messageQueue] count]-1; i++ )
83             {
84                 message_newer = (struct message *)[(NSData *)[[self messageQueue] objectAtIndex: i] bytes];
85                 if( message_newer->type != VLCNotification )
86                     continue;
87                                 
88                 if( message_newer->target == message->target && message_newer->target == message->target && [message_newer->u.name isEqualToString:message->u.name] )
89                 {
90                     [message_newer->target release];
91                     [message->u.name release];
92                     [[self messageQueue] removeObjectAtIndex: i];
93                     i--;
94                 }
95             }
96         }
97
98         dataMessage = [[self messageQueue] lastObject];
99         
100         pthread_mutex_unlock( [self queueLock] );
101         
102         if( message->type == VLCNotification )
103             [self performSelectorOnMainThread:@selector(callDelegateOfObjectAndSendNotificationWithArgs:) withObject:[dataMessage retain] waitUntilDone: NO];
104         else
105             [self performSelectorOnMainThread:@selector(callObjectMethodWithArgs:) withObject:[dataMessage retain] waitUntilDone: NO];
106
107         pthread_mutex_lock( [self queueLock] );
108         [[self messageQueue] removeLastObject];
109         pthread_mutex_unlock( [self queueLock] );
110     
111         [pool release];
112     };
113         return nil;
114 }
115
116 @implementation VLCEventManager
117
118 + (id)sharedManager
119 {
120     /* We do want a lock here to avoid leaks */
121     if ( !defaultManager )
122     {
123         defaultManager = [[VLCEventManager alloc] init];
124     }
125
126     return defaultManager;
127 }
128
129 - (id)init
130 {
131     if( self = [super init] )
132     {
133         pthread_mutex_init( &queueLock, NULL );
134         pthread_cond_init( &signalData, NULL );
135         pthread_create( &dispatcherThread, NULL, EventDispatcherMainLoop, self );
136         messageQueue = [[NSMutableArray alloc] initWithCapacity:10];
137     }
138     return self;
139 }
140
141 - (void)dealloc
142 {
143     pthread_kill( dispatcherThread, SIGKILL );
144     pthread_join( dispatcherThread, NULL );
145
146         [messageQueue release];
147     [super dealloc];
148 }
149
150 - (void)callOnMainThreadDelegateOfObject:(id)aTarget withDelegateMethod:(SEL)aSelector withNotificationName: (NSString *)aNotificationName
151 {
152     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
153     
154         struct message message = 
155         { 
156                 [aTarget retain], 
157                 aSelector, 
158                 [aNotificationName retain], 
159                 VLCNotification 
160         };
161         
162     pthread_mutex_lock( [self queueLock] );
163     [[self messageQueue] insertObject:[NSData dataWithBytes:&message length:sizeof(struct message)] atIndex:0];
164     pthread_cond_signal( [self signalData] );
165     pthread_mutex_unlock( [self queueLock] );
166     
167     [pool release];
168 }
169
170 - (void)callOnMainThreadObject:(id)aTarget withMethod:(SEL)aSelector withArgumentAsObject: (id)arg
171 {
172     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
173
174     struct message message = 
175         { 
176                 [aTarget retain], 
177                 aSelector, 
178                 [arg retain], 
179                 VLCObjectMethodWithObjectArg 
180         };
181         
182     pthread_mutex_lock( [self queueLock] );
183     [[self messageQueue] insertObject:[NSData dataWithBytes:&message length:sizeof(struct message)] atIndex:0];
184     pthread_cond_signal( [self signalData] );
185     pthread_mutex_unlock( [self queueLock] );
186         
187     [pool release];
188 }
189 @end
190
191 @implementation VLCEventManager (PrivateAPI)
192
193 - (void)callDelegateOfObjectAndSendNotificationWithArgs:(NSData*)data
194 {
195     struct message * message = (struct message *)[data bytes];
196
197     [self callDelegateOfObject:message->target withDelegateMethod:message->sel withNotificationName:message->u.name];
198     [message->u.name release];
199     [message->target release];
200     [data release];
201 }
202
203 - (void)callObjectMethodWithArgs:(NSData*)data
204 {
205     struct message * message = (struct message *)[data bytes];
206     void (*method)(id, SEL, id) = (void (*)(id, SEL, id))[message->target methodForSelector: message->sel];
207
208     method( message->target, message->sel, message->u.object);
209     [message->u.object release];
210     [message->target release];
211     [data release];
212 }
213
214 - (NSMutableArray *)messageQueue
215 {
216     return messageQueue;
217 }
218
219 - (pthread_cond_t *)signalData
220 {
221     return &signalData;
222 }
223
224 - (pthread_mutex_t *)queueLock
225 {
226     return &queueLock;
227 }
228
229 - (void)callDelegateOfObject:(id) aTarget withDelegateMethod:(SEL)aSelector withNotificationName: (NSString *)aNotificationName
230 {
231 //    [[NSNotificationCenter defaultCenter] postNotification: [NSNotification notificationWithName:aNotificationName object:aTarget]];
232
233     if (![aTarget delegate] || ![[aTarget delegate] respondsToSelector: aSelector])
234         return;
235
236     void (*method)(id, SEL, id) = (void (*)(id, SEL, id))[[aTarget delegate] methodForSelector: aSelector];
237     method( [aTarget delegate], aSelector, [NSNotification notificationWithName:aNotificationName object:aTarget]);
238 }
239
240 @end