winemac.drv: Disable declaration-after-statement warnings in ObjC files.
[wine.git] / dlls / winemac.drv / cocoa_main.m
blob8c9507a75b1bdd8170e51feaa4d030bf0fcc2e4c
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"
28 #pragma GCC diagnostic ignored "-Wdeclaration-after-statement"
31 /* Condition values for an NSConditionLock. Used to signal between run_cocoa_app
32    and macdrv_start_cocoa_app so the latter knows when the former is running
33    the application event loop. */
34 enum {
35     COCOA_APP_NOT_RUNNING,
36     COCOA_APP_RUNNING,
40 struct cocoa_app_startup_info {
41     NSConditionLock*    lock;
42     unsigned long long  tickcount;
43     uint64_t            uptime_ns;
44     BOOL                success;
48 /***********************************************************************
49  *              run_cocoa_app
50  *
51  * Transforms the main thread from merely idling in its run loop to
52  * being a Cocoa application running its event loop.
53  *
54  * This will be the perform callback of a custom run loop source that
55  * will be scheduled in the main thread's run loop from a secondary
56  * thread by macdrv_start_cocoa_app.  This function communicates that
57  * it has successfully started the application by changing the condition
58  * of a shared NSConditionLock, passed in via the info parameter.
59  *
60  * This function never returns.  It's the new permanent home of the
61  * main thread.
62  */
63 static void run_cocoa_app(void* info)
65     struct cocoa_app_startup_info* startup_info = info;
66     NSConditionLock* lock = startup_info->lock;
67     BOOL created_app = FALSE;
69     @autoreleasepool
70     {
71         if (!NSApp)
72         {
73             [WineApplication sharedApplication];
74             created_app = TRUE;
75         }
77         if ([NSApp respondsToSelector:@selector(setWineController:)])
78         {
79             WineApplicationController* controller = [WineApplicationController sharedController];
80             [NSApp setWineController:controller];
81             [controller computeEventTimeAdjustmentFromTicks:startup_info->tickcount uptime:startup_info->uptime_ns];
82             startup_info->success = TRUE;
83         }
85         /* Retain the lock while we're using it, so macdrv_start_cocoa_app()
86            doesn't deallocate it in the middle of us unlocking it. */
87         [lock retain];
88         [lock lock];
89         [lock unlockWithCondition:COCOA_APP_RUNNING];
90         [lock release];
91     }
93     if (created_app && startup_info->success)
94     {
95         @autoreleasepool
96         {
97             /* Never returns */
98             [NSApp run];
99         }
100     }
104 /***********************************************************************
105  *              macdrv_start_cocoa_app
107  * Tells the main thread to transform itself into a Cocoa application.
109  * Returns 0 on success, non-zero on failure.
110  */
111 int macdrv_start_cocoa_app(unsigned long long tickcount)
113 @autoreleasepool
115     int ret = -1;
116     CFRunLoopSourceRef source;
117     struct cocoa_app_startup_info startup_info;
118     uint64_t uptime_mach = mach_absolute_time();
119     mach_timebase_info_data_t mach_timebase;
120     NSDate* timeLimit;
121     CFRunLoopSourceContext source_context = { 0 };
123     /* Make sure Cocoa is in multi-threading mode by detaching a
124        do-nothing thread. */
125     [NSThread detachNewThreadSelector:@selector(self)
126                              toTarget:[NSThread class]
127                            withObject:nil];
129     startup_info.lock = [[NSConditionLock alloc] initWithCondition:COCOA_APP_NOT_RUNNING];
130     startup_info.tickcount = tickcount;
131     startup_info.success = FALSE;
133     mach_timebase_info(&mach_timebase);
134     startup_info.uptime_ns = uptime_mach * mach_timebase.numer / mach_timebase.denom;
136     timeLimit = [NSDate dateWithTimeIntervalSinceNow:5];
138     source_context.info = &startup_info;
139     source_context.perform = run_cocoa_app;
140     source = CFRunLoopSourceCreate(NULL, 0, &source_context);
142     if (source && startup_info.lock && timeLimit)
143     {
144         CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopCommonModes);
145         CFRunLoopSourceSignal(source);
146         CFRunLoopWakeUp(CFRunLoopGetMain());
148         if ([startup_info.lock lockWhenCondition:COCOA_APP_RUNNING beforeDate:timeLimit])
149         {
150             [startup_info.lock unlock];
151             ret = !startup_info.success;
152         }
153     }
155     if (source)
156         CFRelease(source);
157     [startup_info.lock release];
158     return ret;