imm32: Retrieve the graphics driver module from gdi32.
[wine] / dlls / winemac.drv / cocoa_app.m
1 /*
2  * MACDRV Cocoa application class
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 #import <Carbon/Carbon.h>
22
23 #import "cocoa_app.h"
24 #import "cocoa_event.h"
25 #import "cocoa_window.h"
26
27
28 int macdrv_err_on;
29
30
31 @interface WineApplication ()
32
33 @property (readwrite, copy, nonatomic) NSEvent* lastFlagsChanged;
34
35 @end
36
37
38 @implementation WineApplication
39
40     @synthesize keyboardType, lastFlagsChanged;
41
42     - (id) init
43     {
44         self = [super init];
45         if (self != nil)
46         {
47             eventQueues = [[NSMutableArray alloc] init];
48             eventQueuesLock = [[NSLock alloc] init];
49
50             keyWindows = [[NSMutableArray alloc] init];
51
52             if (!eventQueues || !eventQueuesLock || !keyWindows)
53             {
54                 [self release];
55                 return nil;
56             }
57         }
58         return self;
59     }
60
61     - (void) dealloc
62     {
63         [keyWindows release];
64         [eventQueues release];
65         [eventQueuesLock release];
66         [super dealloc];
67     }
68
69     - (void) transformProcessToForeground
70     {
71         if ([self activationPolicy] != NSApplicationActivationPolicyRegular)
72         {
73             NSMenu* mainMenu;
74             NSMenu* submenu;
75             NSString* bundleName;
76             NSString* title;
77             NSMenuItem* item;
78
79             [self setActivationPolicy:NSApplicationActivationPolicyRegular];
80             [self activateIgnoringOtherApps:YES];
81
82             mainMenu = [[[NSMenu alloc] init] autorelease];
83
84             submenu = [[[NSMenu alloc] initWithTitle:@"Wine"] autorelease];
85             bundleName = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString*)kCFBundleNameKey];
86             if ([bundleName length])
87                 title = [NSString stringWithFormat:@"Quit %@", bundleName];
88             else
89                 title = @"Quit";
90             item = [submenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
91             [item setKeyEquivalentModifierMask:NSCommandKeyMask | NSAlternateKeyMask];
92             item = [[[NSMenuItem alloc] init] autorelease];
93             [item setTitle:@"Wine"];
94             [item setSubmenu:submenu];
95             [mainMenu addItem:item];
96
97             submenu = [[[NSMenu alloc] initWithTitle:@"Window"] autorelease];
98             [submenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@""];
99             [submenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""];
100             [submenu addItem:[NSMenuItem separatorItem]];
101             [submenu addItemWithTitle:@"Bring All to Front" action:@selector(arrangeInFront:) keyEquivalent:@""];
102             item = [[[NSMenuItem alloc] init] autorelease];
103             [item setTitle:@"Window"];
104             [item setSubmenu:submenu];
105             [mainMenu addItem:item];
106
107             [self setMainMenu:mainMenu];
108             [self setWindowsMenu:submenu];
109         }
110     }
111
112     - (BOOL) registerEventQueue:(WineEventQueue*)queue
113     {
114         [eventQueuesLock lock];
115         [eventQueues addObject:queue];
116         [eventQueuesLock unlock];
117         return TRUE;
118     }
119
120     - (void) unregisterEventQueue:(WineEventQueue*)queue
121     {
122         [eventQueuesLock lock];
123         [eventQueues removeObjectIdenticalTo:queue];
124         [eventQueuesLock unlock];
125     }
126
127     - (void) computeEventTimeAdjustmentFromTicks:(unsigned long long)tickcount uptime:(uint64_t)uptime_ns
128     {
129         eventTimeAdjustment = (tickcount / 1000.0) - (uptime_ns / (double)NSEC_PER_SEC);
130     }
131
132     - (double) ticksForEventTime:(NSTimeInterval)eventTime
133     {
134         return (eventTime + eventTimeAdjustment) * 1000;
135     }
136
137     /* Invalidate old focus offers across all queues. */
138     - (void) invalidateGotFocusEvents
139     {
140         WineEventQueue* queue;
141
142         windowFocusSerial++;
143
144         [eventQueuesLock lock];
145         for (queue in eventQueues)
146         {
147             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS)
148                                    forWindow:nil];
149         }
150         [eventQueuesLock unlock];
151     }
152
153     - (void) windowGotFocus:(WineWindow*)window
154     {
155         macdrv_event event;
156
157         [NSApp invalidateGotFocusEvents];
158
159         event.type = WINDOW_GOT_FOCUS;
160         event.window = (macdrv_window)[window retain];
161         event.window_got_focus.serial = windowFocusSerial;
162         if (triedWindows)
163             event.window_got_focus.tried_windows = [triedWindows retain];
164         else
165             event.window_got_focus.tried_windows = [[NSMutableSet alloc] init];
166         [window.queue postEvent:&event];
167     }
168
169     - (void) windowRejectedFocusEvent:(const macdrv_event*)event
170     {
171         if (event->window_got_focus.serial == windowFocusSerial)
172         {
173             triedWindows = (NSMutableSet*)event->window_got_focus.tried_windows;
174             [triedWindows addObject:(WineWindow*)event->window];
175             for (NSWindow* window in [keyWindows arrayByAddingObjectsFromArray:[self orderedWindows]])
176             {
177                 if (![triedWindows containsObject:window] && [window canBecomeKeyWindow])
178                 {
179                     [window makeKeyWindow];
180                     break;
181                 }
182             }
183             triedWindows = nil;
184         }
185     }
186
187     - (void) keyboardSelectionDidChange
188     {
189         TISInputSourceRef inputSource;
190
191         inputSource = TISCopyCurrentKeyboardLayoutInputSource();
192         if (inputSource)
193         {
194             CFDataRef uchr;
195             uchr = TISGetInputSourceProperty(inputSource,
196                     kTISPropertyUnicodeKeyLayoutData);
197             if (uchr)
198             {
199                 macdrv_event event;
200                 WineEventQueue* queue;
201
202                 event.type = KEYBOARD_CHANGED;
203                 event.window = NULL;
204                 event.keyboard_changed.keyboard_type = self.keyboardType;
205                 event.keyboard_changed.iso_keyboard = (KBGetLayoutType(self.keyboardType) == kKeyboardISO);
206                 event.keyboard_changed.uchr = CFDataCreateCopy(NULL, uchr);
207
208                 if (event.keyboard_changed.uchr)
209                 {
210                     [eventQueuesLock lock];
211
212                     for (queue in eventQueues)
213                     {
214                         CFRetain(event.keyboard_changed.uchr);
215                         [queue postEvent:&event];
216                     }
217
218                     [eventQueuesLock unlock];
219
220                     CFRelease(event.keyboard_changed.uchr);
221                 }
222             }
223
224             CFRelease(inputSource);
225         }
226     }
227
228     - (CGFloat) primaryScreenHeight
229     {
230         if (!primaryScreenHeightValid)
231         {
232             NSArray* screens = [NSScreen screens];
233             if ([screens count])
234             {
235                 primaryScreenHeight = NSHeight([[screens objectAtIndex:0] frame]);
236                 primaryScreenHeightValid = TRUE;
237             }
238             else
239                 return 1280; /* arbitrary value */
240         }
241
242         return primaryScreenHeight;
243     }
244
245     - (NSPoint) flippedMouseLocation:(NSPoint)point
246     {
247         /* This relies on the fact that Cocoa's mouse location points are
248            actually off by one (precisely because they were flipped from
249            Quartz screen coordinates using this same technique). */
250         point.y = [self primaryScreenHeight] - point.y;
251         return point;
252     }
253
254
255     /*
256      * ---------- NSApplication method overrides ----------
257      */
258     - (void) sendEvent:(NSEvent*)anEvent
259     {
260         if ([anEvent type] == NSFlagsChanged)
261             self.lastFlagsChanged = anEvent;
262
263         [super sendEvent:anEvent];
264     }
265
266
267     /*
268      * ---------- NSApplicationDelegate methods ----------
269      */
270     - (void)applicationDidChangeScreenParameters:(NSNotification *)notification
271     {
272         primaryScreenHeightValid = FALSE;
273     }
274
275     - (void)applicationDidResignActive:(NSNotification *)notification
276     {
277         macdrv_event event;
278         WineEventQueue* queue;
279
280         [self invalidateGotFocusEvents];
281
282         event.type = APP_DEACTIVATED;
283         event.window = NULL;
284
285         [eventQueuesLock lock];
286         for (queue in eventQueues)
287             [queue postEvent:&event];
288         [eventQueuesLock unlock];
289     }
290
291     - (void)applicationWillFinishLaunching:(NSNotification *)notification
292     {
293         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
294
295         [nc addObserverForName:NSWindowDidBecomeKeyNotification
296                         object:nil
297                          queue:nil
298                     usingBlock:^(NSNotification *note){
299             NSWindow* window = [note object];
300             [keyWindows removeObjectIdenticalTo:window];
301             [keyWindows insertObject:window atIndex:0];
302         }];
303
304         [nc addObserverForName:NSWindowWillCloseNotification
305                         object:nil
306                          queue:[NSOperationQueue mainQueue]
307                     usingBlock:^(NSNotification *note){
308             NSWindow* window = [note object];
309             [keyWindows removeObjectIdenticalTo:window];
310         }];
311
312         [nc addObserver:self
313                selector:@selector(keyboardSelectionDidChange)
314                    name:NSTextInputContextKeyboardSelectionDidChangeNotification
315                  object:nil];
316
317         /* The above notification isn't sent unless the NSTextInputContext
318            class has initialized itself.  Poke it. */
319         [NSTextInputContext self];
320
321         self.keyboardType = LMGetKbdType();
322     }
323
324 @end
325
326 /***********************************************************************
327  *              OnMainThread
328  *
329  * Run a block on the main thread synchronously.
330  */
331 void OnMainThread(dispatch_block_t block)
332 {
333     dispatch_sync(dispatch_get_main_queue(), block);
334 }
335
336 /***********************************************************************
337  *              OnMainThreadAsync
338  *
339  * Run a block on the main thread asynchronously.
340  */
341 void OnMainThreadAsync(dispatch_block_t block)
342 {
343     dispatch_async(dispatch_get_main_queue(), block);
344 }
345
346 /***********************************************************************
347  *              LogError
348  */
349 void LogError(const char* func, NSString* format, ...)
350 {
351     va_list args;
352     va_start(args, format);
353     LogErrorv(func, format, args);
354     va_end(args);
355 }
356
357 /***********************************************************************
358  *              LogErrorv
359  */
360 void LogErrorv(const char* func, NSString* format, va_list args)
361 {
362     NSString* message = [[NSString alloc] initWithFormat:format arguments:args];
363     fprintf(stderr, "err:%s:%s", func, [message UTF8String]);
364     [message release];
365 }
366
367 /***********************************************************************
368  *              macdrv_window_rejected_focus
369  *
370  * Pass focus to the next window that hasn't already rejected this same
371  * WINDOW_GOT_FOCUS event.
372  */
373 void macdrv_window_rejected_focus(const macdrv_event *event)
374 {
375     OnMainThread(^{
376         [NSApp windowRejectedFocusEvent:event];
377     });
378 }
379
380 /***********************************************************************
381  *              macdrv_get_keyboard_layout
382  *
383  * Returns the keyboard layout uchr data.
384  */
385 CFDataRef macdrv_copy_keyboard_layout(CGEventSourceKeyboardType* keyboard_type, int* is_iso)
386 {
387     __block CFDataRef result = NULL;
388
389     OnMainThread(^{
390         TISInputSourceRef inputSource;
391
392         inputSource = TISCopyCurrentKeyboardLayoutInputSource();
393         if (inputSource)
394         {
395             CFDataRef uchr = TISGetInputSourceProperty(inputSource,
396                                 kTISPropertyUnicodeKeyLayoutData);
397             result = CFDataCreateCopy(NULL, uchr);
398             CFRelease(inputSource);
399
400             *keyboard_type = ((WineApplication*)NSApp).keyboardType;
401             *is_iso = (KBGetLayoutType(*keyboard_type) == kKeyboardISO);
402         }
403     });
404
405     return result;
406 }
407
408 /***********************************************************************
409  *              macdrv_beep
410  *
411  * Play the beep sound configured by the user in System Preferences.
412  */
413 void macdrv_beep(void)
414 {
415     OnMainThreadAsync(^{
416         NSBeep();
417     });
418 }