wbemprox: Return an empty object if the path is NULL or empty.
[wine] / dlls / winemac.drv / cocoa_event.m
1 /*
2  * MACDRV Cocoa event queue code
3  *
4  * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <sys/types.h>
22 #include <sys/event.h>
23 #include <sys/time.h>
24 #include <libkern/OSAtomic.h>
25
26 #include "macdrv_cocoa.h"
27 #import "cocoa_event.h"
28 #import "cocoa_app.h"
29 #import "cocoa_window.h"
30
31
32 @interface MacDrvEvent : NSObject
33 {
34 @public
35     macdrv_event event;
36 }
37
38     - (id) initWithEvent:(const macdrv_event*)event;
39
40 @end
41
42 @implementation MacDrvEvent
43
44     - (id) initWithEvent:(const macdrv_event*)inEvent
45     {
46         self = [super init];
47         if (self)
48         {
49             event = *inEvent;
50         }
51         return self;
52     }
53
54 @end
55
56
57 @implementation WineEventQueue
58
59     - (id) init
60     {
61         self = [super init];
62         if (self != nil)
63         {
64             fds[0] = fds[1] = -1;
65
66             events = [[NSMutableArray alloc] init];
67             eventsLock = [[NSLock alloc] init];
68
69             if (!events || !eventsLock)
70             {
71                 [self release];
72                 return nil;
73             }
74
75             if (pipe(fds) ||
76                 fcntl(fds[0], F_SETFD, 1) == -1 ||
77                 fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 ||
78                 fcntl(fds[1], F_SETFD, 1) == -1 ||
79                 fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1)
80             {
81                 [self release];
82                 return nil;
83             }
84         }
85         return self;
86     }
87
88     - (void) dealloc
89     {
90         [events release];
91         [eventsLock release];
92
93         if (fds[0] != -1) close(fds[0]);
94         if (fds[1] != -1) close(fds[1]);
95
96         [super dealloc];
97     }
98
99     - (void) signalEventAvailable
100     {
101         char junk = 1;
102         int rc;
103
104         do
105         {
106             rc = write(fds[1], &junk, 1);
107         } while (rc < 0 && errno == EINTR);
108
109         if (rc < 0 && errno != EAGAIN)
110             ERR(@"%@: got error writing to event queue signaling pipe: %s\n", self, strerror(errno));
111     }
112
113     - (void) postEventObject:(MacDrvEvent*)event
114     {
115         [eventsLock lock];
116         [events addObject:event];
117         [eventsLock unlock];
118
119         [self signalEventAvailable];
120     }
121
122     - (void) postEvent:(const macdrv_event*)inEvent
123     {
124         MacDrvEvent* event = [[MacDrvEvent alloc] initWithEvent:inEvent];
125         [self postEventObject:event];
126         [event release];
127     }
128
129     - (MacDrvEvent*) getEventMatchingMask:(macdrv_event_mask)mask
130     {
131         char buf[512];
132         int rc;
133         NSUInteger index;
134         MacDrvEvent* event;
135
136         /* Clear the pipe which signals there are pending events. */
137         do
138         {
139             rc = read(fds[0], buf, sizeof(buf));
140         } while (rc > 0 || (rc < 0 && errno == EINTR));
141         if (rc == 0 || (rc < 0 && errno != EAGAIN))
142         {
143             if (rc == 0)
144                 ERR(@"%@: event queue signaling pipe unexpectedly closed\n", self);
145             else
146                 ERR(@"%@: got error reading from event queue signaling pipe: %s\n", self, strerror(errno));
147             return nil;
148         }
149
150         [eventsLock lock];
151
152         index = 0;
153         for (event in events)
154         {
155             if (event_mask_for_type(event->event.type) & mask)
156                 break;
157
158             index++;
159         }
160
161         if (event)
162         {
163             [event retain];
164             [events removeObjectAtIndex:index];
165         }
166
167         [eventsLock unlock];
168         return [event autorelease];
169     }
170
171     - (void) discardEventsMatchingMask:(macdrv_event_mask)mask forWindow:(NSWindow*)window
172     {
173         NSMutableIndexSet* indexes = [[[NSMutableIndexSet alloc] init] autorelease];
174
175         [eventsLock lock];
176
177         [events enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
178             MacDrvEvent* event = obj;
179             if ((event_mask_for_type(event->event.type) & mask) &&
180                 (!window || event->event.window == (macdrv_window)window))
181             {
182                 macdrv_cleanup_event(&event->event);
183                 [indexes addIndex:idx];
184             }
185         }];
186
187         [events removeObjectsAtIndexes:indexes];
188
189         [eventsLock unlock];
190     }
191
192
193 /***********************************************************************
194  *              macdrv_create_event_queue
195  *
196  * Register this thread with the application on the main thread, and set
197  * up an event queue on which it can deliver events to this thread.
198  */
199 macdrv_event_queue macdrv_create_event_queue(void)
200 {
201     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
202
203     WineEventQueue* queue = [[WineEventQueue alloc] init];
204     if (queue && ![NSApp registerEventQueue:queue])
205     {
206         [queue release];
207         queue = nil;
208     }
209
210     [pool release];
211     return (macdrv_event_queue)queue;
212 }
213
214 /***********************************************************************
215  *              macdrv_destroy_event_queue
216  *
217  * Tell the application that this thread is exiting and destroy the
218  * associated event queue.
219  */
220 void macdrv_destroy_event_queue(macdrv_event_queue queue)
221 {
222     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
223     WineEventQueue* q = (WineEventQueue*)queue;
224
225     [NSApp unregisterEventQueue:q];
226     [q release];
227
228     [pool release];
229 }
230
231 /***********************************************************************
232  *              macdrv_get_event_queue_fd
233  *
234  * Get the file descriptor whose readability signals that there are
235  * events on the event queue.
236  */
237 int macdrv_get_event_queue_fd(macdrv_event_queue queue)
238 {
239     WineEventQueue* q = (WineEventQueue*)queue;
240     return q->fds[0];
241 }
242
243 /***********************************************************************
244  *              macdrv_get_event_from_queue
245  *
246  * Pull an event matching the event mask from the event queue and store
247  * it in the event record pointed to by the event parameter.  If a
248  * matching event was found, return non-zero; otherwise, return 0.
249  *
250  * The caller is responsible for calling macdrv_cleanup_event on any
251  * event returned by this function.
252  */
253 int macdrv_get_event_from_queue(macdrv_event_queue queue,
254         macdrv_event_mask mask, macdrv_event *event)
255 {
256     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
257     WineEventQueue* q = (WineEventQueue*)queue;
258
259     MacDrvEvent* macDrvEvent = [q getEventMatchingMask:mask];
260     if (macDrvEvent)
261         *event = macDrvEvent->event;
262
263     [pool release];
264     return (macDrvEvent != nil);
265 }
266
267 /***********************************************************************
268  *              macdrv_cleanup_event
269  *
270  * Performs cleanup of an event.  For event types which carry resources
271  * such as allocated memory or retained objects, frees/releases those
272  * resources.
273  */
274 void macdrv_cleanup_event(macdrv_event *event)
275 {
276     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
277
278     switch (event->type)
279     {
280         case WINDOW_GOT_FOCUS:
281             [(NSMutableSet*)event->window_got_focus.tried_windows release];
282             break;
283     }
284
285     [(WineWindow*)event->window release];
286
287     [pool release];
288 }
289
290 @end