MacOSX/Framework/VLCEventManager.m: Directly issue events to main thread if we are...
[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 (Private)
48 - (void)callDelegateOfObjectAndSendNotificationWithArgs:(NSData*)data;
49 - (void)callObjectMethodWithArgs:(NSData*)data;
50 - (void)callDelegateOfObject:(id) aTarget withDelegateMethod:(SEL)aSelector withNotificationName: (NSString *)aNotificationName;
51 - (pthread_cond_t *)signalData;
52 - (pthread_mutex_t *)queueLock;
53 - (NSMutableArray *)messageQueue;
54 @end
55
56 static void * EventDispatcherMainLoop(void * user_data)
57 {
58     VLCEventManager * self = user_data;
59
60     for(;;)
61     {
62         NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
63         struct message * message, * message_newer = NULL;
64         NSData *dataMessage;
65         int i;
66
67         /* Wait for some data */
68         pthread_mutex_lock( [self queueLock] );
69         
70         /* Wait until we have something on the queue */
71         while([[self messageQueue] count] <= 0 )
72         {
73             pthread_cond_wait( [self signalData], [self queueLock] );
74         }
75         message = (struct message *)[(NSData *)[[self messageQueue] lastObject] bytes];
76         
77         /* Don't send the same notification twice */
78         if( message->type == VLCNotification )
79         {
80             for( i = 0; i < [[self messageQueue] count]-1; i++ )
81             {
82                 message_newer = (struct message *)[(NSData *)[[self messageQueue] objectAtIndex: i] bytes];
83                 if( message_newer->type != VLCNotification )
84                     continue;
85                 
86                 if( message_newer->target == message->target && message_newer->target == message->target && [message_newer->u.name isEqualToString:message->u.name] )
87                 {
88                     [message_newer->target release];
89                     [message->u.name release];
90                     [[self messageQueue] removeObjectAtIndex: i];
91                     i--;
92                 }
93             }
94         }
95
96         dataMessage = [[self messageQueue] lastObject];
97         
98         pthread_mutex_unlock( [self queueLock] );
99         
100         if( message->type == VLCNotification )
101             [self performSelectorOnMainThread:@selector(callDelegateOfObjectAndSendNotificationWithArgs:) withObject:[dataMessage retain]  /* released in the call */ waitUntilDone: NO];
102         else
103             [self performSelectorOnMainThread:@selector(callObjectMethodWithArgs:) withObject:[dataMessage retain]  /* released in the call */ waitUntilDone: NO];
104
105         pthread_mutex_lock( [self queueLock] );
106         [[self messageQueue] removeLastObject];
107         pthread_mutex_unlock( [self queueLock] );
108     
109         [pool release];
110     }
111     return nil;
112 }
113
114 @implementation VLCEventManager
115 + (id)sharedManager
116 {
117     /* We do want a lock here to avoid leaks */
118     if ( !defaultManager )
119     {
120         defaultManager = [[VLCEventManager alloc] init];
121     }
122
123     return defaultManager;
124 }
125
126 - (id)init
127 {
128     if( self = [super init] )
129     {
130         pthread_mutex_init( &queueLock, NULL );
131         pthread_cond_init( &signalData, NULL );
132         pthread_create( &dispatcherThread, NULL, EventDispatcherMainLoop, self );
133         messageQueue = [[NSMutableArray alloc] initWithCapacity:10];
134     }
135     return self;
136 }
137
138 - (void)dealloc
139 {
140     pthread_kill( dispatcherThread, SIGKILL );
141     pthread_join( dispatcherThread, NULL );
142
143     [messageQueue release];
144     [super dealloc];
145 }
146
147 - (void)callOnMainThreadDelegateOfObject:(id)aTarget withDelegateMethod:(SEL)aSelector withNotificationName: (NSString *)aNotificationName
148 {
149     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
150     
151     struct message message = 
152     { 
153         [aTarget retain], 
154         aSelector, 
155         [aNotificationName retain], 
156         VLCNotification 
157     };
158
159     if([NSThread isMainThread])
160     {
161         [self callDelegateOfObjectAndSendNotificationWithArgs:[[NSData dataWithBytes:&message length:sizeof(struct message)] retain] /* released in the call */];
162         return;
163     }
164
165     pthread_mutex_lock( [self queueLock] );
166     [[self messageQueue] insertObject:[NSData dataWithBytes:&message length:sizeof(struct message)] atIndex:0];
167     pthread_cond_signal( [self signalData] );
168     pthread_mutex_unlock( [self queueLock] );
169     
170     [pool release];
171 }
172
173 - (void)callOnMainThreadObject:(id)aTarget withMethod:(SEL)aSelector withArgumentAsObject: (id)arg
174 {
175     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
176     struct message message = 
177     { 
178         [aTarget retain], 
179         aSelector, 
180         [arg retain], 
181         VLCObjectMethodWithObjectArg 
182     };
183
184     if([NSThread isMainThread])
185     {
186         [self callObjectMethodWithArgs:[[NSData dataWithBytes:&message length:sizeof(struct message)] retain] /* released in the call */];
187         return;
188     }
189     
190     pthread_mutex_lock( [self queueLock] );
191     [[self messageQueue] insertObject:[NSData dataWithBytes:&message length:sizeof(struct message)] atIndex:0];
192     pthread_cond_signal( [self signalData] );
193     pthread_mutex_unlock( [self queueLock] );
194     
195     [pool release];
196 }
197 @end
198
199 @implementation VLCEventManager (Private)
200 - (void)callDelegateOfObjectAndSendNotificationWithArgs:(NSData*)data
201 {
202     struct message * message = (struct message *)[data bytes];
203
204     [self callDelegateOfObject:message->target withDelegateMethod:message->sel withNotificationName:message->u.name];
205     [message->u.name release];
206     [message->target release];
207     [data release];
208 }
209
210 - (void)callObjectMethodWithArgs:(NSData*)data
211 {
212     struct message * message = (struct message *)[data bytes];
213     void (*method)(id, SEL, id) = (void (*)(id, SEL, id))[message->target methodForSelector: message->sel];
214
215     method( message->target, message->sel, message->u.object);
216     [message->u.object release];
217     [message->target release];
218     [data release];
219 }
220
221 - (NSMutableArray *)messageQueue
222 {
223     return messageQueue;
224 }
225
226 - (pthread_cond_t *)signalData
227 {
228     return &signalData;
229 }
230
231 - (pthread_mutex_t *)queueLock
232 {
233     return &queueLock;
234 }
235
236 - (void)callDelegateOfObject:(id) aTarget withDelegateMethod:(SEL)aSelector withNotificationName: (NSString *)aNotificationName
237 {
238 //    [[NSNotificationCenter defaultCenter] postNotification: [NSNotification notificationWithName:aNotificationName object:aTarget]];
239
240     if (![aTarget delegate] || ![[aTarget delegate] respondsToSelector: aSelector])
241         return;
242
243     void (*method)(id, SEL, id) = (void (*)(id, SEL, id))[[aTarget delegate] methodForSelector: aSelector];
244     method( [aTarget delegate], aSelector, [NSNotification notificationWithName:aNotificationName object:aTarget]);
245 }
246 @end