winemac: Use unixlib interface for dragdrop.c calls.
[wine.git] / dlls / winemac.drv / cocoa_main.m
blob6682f98759b59f608b39b01ea37817d760319bc3
1 /*
2  * MACDRV Cocoa initialization 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  */
21 #import <AppKit/AppKit.h>
22 #include <mach/mach.h>
23 #include <mach/mach_time.h>
25 #include "macdrv_cocoa.h"
26 #import "cocoa_app.h"
29 /* Condition values for an NSConditionLock. Used to signal between run_cocoa_app
30    and macdrv_start_cocoa_app so the latter knows when the former is running
31    the application event loop. */
32 enum {
33     COCOA_APP_NOT_RUNNING,
34     COCOA_APP_RUNNING,
38 struct cocoa_app_startup_info {
39     NSConditionLock*    lock;
40     unsigned long long  tickcount;
41     uint64_t            uptime_ns;
42     BOOL                success;
46 /***********************************************************************
47  *              run_cocoa_app
48  *
49  * Transforms the main thread from merely idling in its run loop to
50  * being a Cocoa application running its event loop.
51  *
52  * This will be the perform callback of a custom run loop source that
53  * will be scheduled in the main thread's run loop from a secondary
54  * thread by macdrv_start_cocoa_app.  This function communicates that
55  * it has successfully started the application by changing the condition
56  * of a shared NSConditionLock, passed in via the info parameter.
57  *
58  * This function never returns.  It's the new permanent home of the
59  * main thread.
60  */
61 static void run_cocoa_app(void* info)
63     struct cocoa_app_startup_info* startup_info = info;
64     NSConditionLock* lock = startup_info->lock;
65     BOOL created_app = FALSE;
67     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
69     if (!NSApp)
70     {
71         [WineApplication sharedApplication];
72         created_app = TRUE;
73     }
75     if ([NSApp respondsToSelector:@selector(setWineController:)])
76     {
77         WineApplicationController* controller = [WineApplicationController sharedController];
78         [NSApp setWineController:controller];
79         [controller computeEventTimeAdjustmentFromTicks:startup_info->tickcount uptime:startup_info->uptime_ns];
80         startup_info->success = TRUE;
81     }
83     /* Retain the lock while we're using it, so macdrv_start_cocoa_app()
84        doesn't deallocate it in the middle of us unlocking it. */
85     [lock retain];
86     [lock lock];
87     [lock unlockWithCondition:COCOA_APP_RUNNING];
88     [lock release];
90     [pool release];
92     if (created_app && startup_info->success)
93     {
94         /* Never returns */
95         [NSApp run];
96     }
100 /***********************************************************************
101  *              macdrv_start_cocoa_app
103  * Tells the main thread to transform itself into a Cocoa application.
105  * Returns 0 on success, non-zero on failure.
106  */
107 int macdrv_start_cocoa_app(unsigned long long tickcount)
109     int ret = -1;
110     CFRunLoopSourceRef source;
111     struct cocoa_app_startup_info startup_info;
112     uint64_t uptime_mach = mach_absolute_time();
113     mach_timebase_info_data_t mach_timebase;
114     NSDate* timeLimit;
115     CFRunLoopSourceContext source_context = { 0 };
117     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
119     /* Make sure Cocoa is in multi-threading mode by detaching a
120        do-nothing thread. */
121     [NSThread detachNewThreadSelector:@selector(self)
122                              toTarget:[NSThread class]
123                            withObject:nil];
125     startup_info.lock = [[NSConditionLock alloc] initWithCondition:COCOA_APP_NOT_RUNNING];
126     startup_info.tickcount = tickcount;
127     startup_info.success = FALSE;
129     mach_timebase_info(&mach_timebase);
130     startup_info.uptime_ns = uptime_mach * mach_timebase.numer / mach_timebase.denom;
132     timeLimit = [NSDate dateWithTimeIntervalSinceNow:5];
134     source_context.info = &startup_info;
135     source_context.perform = run_cocoa_app;
136     source = CFRunLoopSourceCreate(NULL, 0, &source_context);
138     if (source && startup_info.lock && timeLimit)
139     {
140         CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopCommonModes);
141         CFRunLoopSourceSignal(source);
142         CFRunLoopWakeUp(CFRunLoopGetMain());
144         if ([startup_info.lock lockWhenCondition:COCOA_APP_RUNNING beforeDate:timeLimit])
145         {
146             [startup_info.lock unlock];
147             ret = !startup_info.success;
148         }
149     }
151     if (source)
152         CFRelease(source);
153     [startup_info.lock release];
154     [pool release];
155     return ret;