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