wine/dlls/winemac.drv/cocoa_main.m

157 lines
4.9 KiB
Objective-C

/*
* MACDRV Cocoa initialization code
*
* 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 <AppKit/AppKit.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include "macdrv_cocoa.h"
#import "cocoa_app.h"
/* Condition values for an NSConditionLock. Used to signal between run_cocoa_app
and macdrv_start_cocoa_app so the latter knows when the former is running
the application event loop. */
enum {
COCOA_APP_NOT_RUNNING,
COCOA_APP_RUNNING,
};
struct cocoa_app_startup_info {
NSConditionLock* lock;
unsigned long long tickcount;
uint64_t uptime_ns;
BOOL success;
};
/***********************************************************************
* run_cocoa_app
*
* Transforms the main thread from merely idling in its run loop to
* being a Cocoa application running its event loop.
*
* This will be the perform callback of a custom run loop source that
* will be scheduled in the main thread's run loop from a secondary
* thread by macdrv_start_cocoa_app. This function communicates that
* it has successfully started the application by changing the condition
* of a shared NSConditionLock, passed in via the info parameter.
*
* This function never returns. It's the new permanent home of the
* main thread.
*/
static void run_cocoa_app(void* info)
{
struct cocoa_app_startup_info* startup_info = info;
NSConditionLock* lock = startup_info->lock;
BOOL created_app = FALSE;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (!NSApp)
{
[WineApplication sharedApplication];
created_app = TRUE;
}
if ([NSApp respondsToSelector:@selector(setWineController:)])
{
WineApplicationController* controller = [WineApplicationController sharedController];
[NSApp setWineController:controller];
[controller computeEventTimeAdjustmentFromTicks:startup_info->tickcount uptime:startup_info->uptime_ns];
startup_info->success = TRUE;
}
/* Retain the lock while we're using it, so macdrv_start_cocoa_app()
doesn't deallocate it in the middle of us unlocking it. */
[lock retain];
[lock lock];
[lock unlockWithCondition:COCOA_APP_RUNNING];
[lock release];
[pool release];
if (created_app && startup_info->success)
{
/* Never returns */
[NSApp run];
}
}
/***********************************************************************
* macdrv_start_cocoa_app
*
* Tells the main thread to transform itself into a Cocoa application.
*
* Returns 0 on success, non-zero on failure.
*/
int macdrv_start_cocoa_app(unsigned long long tickcount)
{
int ret = -1;
CFRunLoopSourceRef source;
struct cocoa_app_startup_info startup_info;
uint64_t uptime_mach = mach_absolute_time();
mach_timebase_info_data_t mach_timebase;
NSDate* timeLimit;
CFRunLoopSourceContext source_context = { 0 };
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
/* Make sure Cocoa is in multi-threading mode by detaching a
do-nothing thread. */
[NSThread detachNewThreadSelector:@selector(self)
toTarget:[NSThread class]
withObject:nil];
startup_info.lock = [[NSConditionLock alloc] initWithCondition:COCOA_APP_NOT_RUNNING];
startup_info.tickcount = tickcount;
startup_info.success = FALSE;
mach_timebase_info(&mach_timebase);
startup_info.uptime_ns = uptime_mach * mach_timebase.numer / mach_timebase.denom;
timeLimit = [NSDate dateWithTimeIntervalSinceNow:5];
source_context.info = &startup_info;
source_context.perform = run_cocoa_app;
source = CFRunLoopSourceCreate(NULL, 0, &source_context);
if (source && startup_info.lock && timeLimit)
{
CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopCommonModes);
CFRunLoopSourceSignal(source);
CFRunLoopWakeUp(CFRunLoopGetMain());
if ([startup_info.lock lockWhenCondition:COCOA_APP_RUNNING beforeDate:timeLimit])
{
[startup_info.lock unlock];
ret = !startup_info.success;
}
}
if (source)
CFRelease(source);
[startup_info.lock release];
[pool release];
return ret;
}