/* * MACDRV Cocoa application class * * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #import "cocoa_app.h" #import "cocoa_event.h" #import "cocoa_window.h" int macdrv_err_on; @implementation WineApplication - (id) init { self = [super init]; if (self != nil) { eventQueues = [[NSMutableArray alloc] init]; eventQueuesLock = [[NSLock alloc] init]; keyWindows = [[NSMutableArray alloc] init]; if (!eventQueues || !eventQueuesLock || !keyWindows) { [self release]; return nil; } } return self; } - (void) dealloc { [keyWindows release]; [eventQueues release]; [eventQueuesLock release]; [super dealloc]; } - (void) transformProcessToForeground { if ([self activationPolicy] != NSApplicationActivationPolicyRegular) { NSMenu* mainMenu; NSMenu* submenu; NSString* bundleName; NSString* title; NSMenuItem* item; [self setActivationPolicy:NSApplicationActivationPolicyRegular]; [self activateIgnoringOtherApps:YES]; mainMenu = [[[NSMenu alloc] init] autorelease]; submenu = [[[NSMenu alloc] initWithTitle:@"Wine"] autorelease]; bundleName = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString*)kCFBundleNameKey]; if ([bundleName length]) title = [NSString stringWithFormat:@"Quit %@", bundleName]; else title = @"Quit"; item = [submenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; [item setKeyEquivalentModifierMask:NSCommandKeyMask | NSAlternateKeyMask]; item = [[[NSMenuItem alloc] init] autorelease]; [item setTitle:@"Wine"]; [item setSubmenu:submenu]; [mainMenu addItem:item]; submenu = [[[NSMenu alloc] initWithTitle:@"Window"] autorelease]; [submenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@""]; [submenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""]; [submenu addItem:[NSMenuItem separatorItem]]; [submenu addItemWithTitle:@"Bring All to Front" action:@selector(arrangeInFront:) keyEquivalent:@""]; item = [[[NSMenuItem alloc] init] autorelease]; [item setTitle:@"Window"]; [item setSubmenu:submenu]; [mainMenu addItem:item]; [self setMainMenu:mainMenu]; [self setWindowsMenu:submenu]; } } - (BOOL) registerEventQueue:(WineEventQueue*)queue { [eventQueuesLock lock]; [eventQueues addObject:queue]; [eventQueuesLock unlock]; return TRUE; } - (void) unregisterEventQueue:(WineEventQueue*)queue { [eventQueuesLock lock]; [eventQueues removeObjectIdenticalTo:queue]; [eventQueuesLock unlock]; } - (void) computeEventTimeAdjustmentFromTicks:(unsigned long long)tickcount uptime:(uint64_t)uptime_ns { eventTimeAdjustment = (tickcount / 1000.0) - (uptime_ns / (double)NSEC_PER_SEC); } - (double) ticksForEventTime:(NSTimeInterval)eventTime { return (eventTime + eventTimeAdjustment) * 1000; } /* Invalidate old focus offers across all queues. */ - (void) invalidateGotFocusEvents { WineEventQueue* queue; windowFocusSerial++; [eventQueuesLock lock]; for (queue in eventQueues) { [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) forWindow:nil]; } [eventQueuesLock unlock]; } - (void) windowGotFocus:(WineWindow*)window { macdrv_event event; [NSApp invalidateGotFocusEvents]; event.type = WINDOW_GOT_FOCUS; event.window = (macdrv_window)[window retain]; event.window_got_focus.serial = windowFocusSerial; if (triedWindows) event.window_got_focus.tried_windows = [triedWindows retain]; else event.window_got_focus.tried_windows = [[NSMutableSet alloc] init]; [window.queue postEvent:&event]; } - (void) windowRejectedFocusEvent:(const macdrv_event*)event { if (event->window_got_focus.serial == windowFocusSerial) { triedWindows = (NSMutableSet*)event->window_got_focus.tried_windows; [triedWindows addObject:(WineWindow*)event->window]; for (NSWindow* window in [keyWindows arrayByAddingObjectsFromArray:[self orderedWindows]]) { if (![triedWindows containsObject:window] && [window canBecomeKeyWindow]) { [window makeKeyWindow]; break; } } triedWindows = nil; } } /* * ---------- NSApplicationDelegate methods ---------- */ - (void)applicationDidResignActive:(NSNotification *)notification { [self invalidateGotFocusEvents]; } - (void)applicationWillFinishLaunching:(NSNotification *)notification { NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; [nc addObserverForName:NSWindowDidBecomeKeyNotification object:nil queue:nil usingBlock:^(NSNotification *note){ NSWindow* window = [note object]; [keyWindows removeObjectIdenticalTo:window]; [keyWindows insertObject:window atIndex:0]; }]; [nc addObserverForName:NSWindowWillCloseNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note){ NSWindow* window = [note object]; [keyWindows removeObjectIdenticalTo:window]; }]; } @end /*********************************************************************** * OnMainThread * * Run a block on the main thread synchronously. */ void OnMainThread(dispatch_block_t block) { dispatch_sync(dispatch_get_main_queue(), block); } /*********************************************************************** * OnMainThreadAsync * * Run a block on the main thread asynchronously. */ void OnMainThreadAsync(dispatch_block_t block) { dispatch_async(dispatch_get_main_queue(), block); } /*********************************************************************** * LogError */ void LogError(const char* func, NSString* format, ...) { va_list args; va_start(args, format); LogErrorv(func, format, args); va_end(args); } /*********************************************************************** * LogErrorv */ void LogErrorv(const char* func, NSString* format, va_list args) { NSString* message = [[NSString alloc] initWithFormat:format arguments:args]; fprintf(stderr, "err:%s:%s", func, [message UTF8String]); [message release]; } /*********************************************************************** * macdrv_window_rejected_focus * * Pass focus to the next window that hasn't already rejected this same * WINDOW_GOT_FOCUS event. */ void macdrv_window_rejected_focus(const macdrv_event *event) { OnMainThread(^{ [NSApp windowRejectedFocusEvent:event]; }); }