2 * MACDRV Cocoa application class
4 * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
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.
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.
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
21 #import <Carbon/Carbon.h>
24 #import "cocoa_event.h"
25 #import "cocoa_window.h"
31 @interface WineApplication ()
33 @property (readwrite, copy, nonatomic) NSEvent* lastFlagsChanged;
38 @implementation WineApplication
40 @synthesize keyboardType, lastFlagsChanged;
47 eventQueues = [[NSMutableArray alloc] init];
48 eventQueuesLock = [[NSLock alloc] init];
50 keyWindows = [[NSMutableArray alloc] init];
52 if (!eventQueues || !eventQueuesLock || !keyWindows)
64 [eventQueues release];
65 [eventQueuesLock release];
69 - (void) transformProcessToForeground
71 if ([self activationPolicy] != NSApplicationActivationPolicyRegular)
79 [self setActivationPolicy:NSApplicationActivationPolicyRegular];
80 [self activateIgnoringOtherApps:YES];
82 mainMenu = [[[NSMenu alloc] init] autorelease];
84 submenu = [[[NSMenu alloc] initWithTitle:@"Wine"] autorelease];
85 bundleName = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString*)kCFBundleNameKey];
86 if ([bundleName length])
87 title = [NSString stringWithFormat:@"Quit %@", bundleName];
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];
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];
107 [self setMainMenu:mainMenu];
108 [self setWindowsMenu:submenu];
112 - (BOOL) registerEventQueue:(WineEventQueue*)queue
114 [eventQueuesLock lock];
115 [eventQueues addObject:queue];
116 [eventQueuesLock unlock];
120 - (void) unregisterEventQueue:(WineEventQueue*)queue
122 [eventQueuesLock lock];
123 [eventQueues removeObjectIdenticalTo:queue];
124 [eventQueuesLock unlock];
127 - (void) computeEventTimeAdjustmentFromTicks:(unsigned long long)tickcount uptime:(uint64_t)uptime_ns
129 eventTimeAdjustment = (tickcount / 1000.0) - (uptime_ns / (double)NSEC_PER_SEC);
132 - (double) ticksForEventTime:(NSTimeInterval)eventTime
134 return (eventTime + eventTimeAdjustment) * 1000;
137 /* Invalidate old focus offers across all queues. */
138 - (void) invalidateGotFocusEvents
140 WineEventQueue* queue;
144 [eventQueuesLock lock];
145 for (queue in eventQueues)
147 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS)
150 [eventQueuesLock unlock];
153 - (void) windowGotFocus:(WineWindow*)window
157 [NSApp invalidateGotFocusEvents];
159 event.type = WINDOW_GOT_FOCUS;
160 event.window = (macdrv_window)[window retain];
161 event.window_got_focus.serial = windowFocusSerial;
163 event.window_got_focus.tried_windows = [triedWindows retain];
165 event.window_got_focus.tried_windows = [[NSMutableSet alloc] init];
166 [window.queue postEvent:&event];
169 - (void) windowRejectedFocusEvent:(const macdrv_event*)event
171 if (event->window_got_focus.serial == windowFocusSerial)
173 triedWindows = (NSMutableSet*)event->window_got_focus.tried_windows;
174 [triedWindows addObject:(WineWindow*)event->window];
175 for (NSWindow* window in [keyWindows arrayByAddingObjectsFromArray:[self orderedWindows]])
177 if (![triedWindows containsObject:window] && [window canBecomeKeyWindow])
179 [window makeKeyWindow];
187 - (void) keyboardSelectionDidChange
189 TISInputSourceRef inputSource;
191 inputSource = TISCopyCurrentKeyboardLayoutInputSource();
195 uchr = TISGetInputSourceProperty(inputSource,
196 kTISPropertyUnicodeKeyLayoutData);
200 WineEventQueue* queue;
202 event.type = KEYBOARD_CHANGED;
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);
208 if (event.keyboard_changed.uchr)
210 [eventQueuesLock lock];
212 for (queue in eventQueues)
214 CFRetain(event.keyboard_changed.uchr);
215 [queue postEvent:&event];
218 [eventQueuesLock unlock];
220 CFRelease(event.keyboard_changed.uchr);
224 CFRelease(inputSource);
228 - (CGFloat) primaryScreenHeight
230 if (!primaryScreenHeightValid)
232 NSArray* screens = [NSScreen screens];
235 primaryScreenHeight = NSHeight([[screens objectAtIndex:0] frame]);
236 primaryScreenHeightValid = TRUE;
239 return 1280; /* arbitrary value */
242 return primaryScreenHeight;
245 - (NSPoint) flippedMouseLocation:(NSPoint)point
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;
256 * ---------- NSApplication method overrides ----------
258 - (void) sendEvent:(NSEvent*)anEvent
260 if ([anEvent type] == NSFlagsChanged)
261 self.lastFlagsChanged = anEvent;
263 [super sendEvent:anEvent];
268 * ---------- NSApplicationDelegate methods ----------
270 - (void)applicationDidChangeScreenParameters:(NSNotification *)notification
272 primaryScreenHeightValid = FALSE;
275 - (void)applicationDidResignActive:(NSNotification *)notification
278 WineEventQueue* queue;
280 [self invalidateGotFocusEvents];
282 event.type = APP_DEACTIVATED;
285 [eventQueuesLock lock];
286 for (queue in eventQueues)
287 [queue postEvent:&event];
288 [eventQueuesLock unlock];
291 - (void)applicationWillFinishLaunching:(NSNotification *)notification
293 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
295 [nc addObserverForName:NSWindowDidBecomeKeyNotification
298 usingBlock:^(NSNotification *note){
299 NSWindow* window = [note object];
300 [keyWindows removeObjectIdenticalTo:window];
301 [keyWindows insertObject:window atIndex:0];
304 [nc addObserverForName:NSWindowWillCloseNotification
306 queue:[NSOperationQueue mainQueue]
307 usingBlock:^(NSNotification *note){
308 NSWindow* window = [note object];
309 [keyWindows removeObjectIdenticalTo:window];
313 selector:@selector(keyboardSelectionDidChange)
314 name:NSTextInputContextKeyboardSelectionDidChangeNotification
317 /* The above notification isn't sent unless the NSTextInputContext
318 class has initialized itself. Poke it. */
319 [NSTextInputContext self];
321 self.keyboardType = LMGetKbdType();
326 /***********************************************************************
329 * Run a block on the main thread synchronously.
331 void OnMainThread(dispatch_block_t block)
333 dispatch_sync(dispatch_get_main_queue(), block);
336 /***********************************************************************
339 * Run a block on the main thread asynchronously.
341 void OnMainThreadAsync(dispatch_block_t block)
343 dispatch_async(dispatch_get_main_queue(), block);
346 /***********************************************************************
349 void LogError(const char* func, NSString* format, ...)
352 va_start(args, format);
353 LogErrorv(func, format, args);
357 /***********************************************************************
360 void LogErrorv(const char* func, NSString* format, va_list args)
362 NSString* message = [[NSString alloc] initWithFormat:format arguments:args];
363 fprintf(stderr, "err:%s:%s", func, [message UTF8String]);
367 /***********************************************************************
368 * macdrv_window_rejected_focus
370 * Pass focus to the next window that hasn't already rejected this same
371 * WINDOW_GOT_FOCUS event.
373 void macdrv_window_rejected_focus(const macdrv_event *event)
376 [NSApp windowRejectedFocusEvent:event];
380 /***********************************************************************
381 * macdrv_get_keyboard_layout
383 * Returns the keyboard layout uchr data.
385 CFDataRef macdrv_copy_keyboard_layout(CGEventSourceKeyboardType* keyboard_type, int* is_iso)
387 __block CFDataRef result = NULL;
390 TISInputSourceRef inputSource;
392 inputSource = TISCopyCurrentKeyboardLayoutInputSource();
395 CFDataRef uchr = TISGetInputSourceProperty(inputSource,
396 kTISPropertyUnicodeKeyLayoutData);
397 result = CFDataCreateCopy(NULL, uchr);
398 CFRelease(inputSource);
400 *keyboard_type = ((WineApplication*)NSApp).keyboardType;
401 *is_iso = (KBGetLayoutType(*keyboard_type) == kKeyboardISO);
408 /***********************************************************************
411 * Play the beep sound configured by the user in System Preferences.
413 void macdrv_beep(void)