winemac: Add support for delay-rendered (a.k.a. promised) clipboard data.
[wine] / dlls / winemac.drv / cocoa_main.m
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  */
20
21 #import <AppKit/AppKit.h>
22 #include <mach/mach.h>
23 #include <mach/mach_time.h>
24
25 #include "macdrv_cocoa.h"
26 #import "cocoa_app.h"
27
28
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,
35 };
36
37
38 struct cocoa_app_startup_info {
39     NSConditionLock*    lock;
40     unsigned long long  tickcount;
41     uint64_t            uptime_ns;
42 };
43
44
45 /***********************************************************************
46  *              run_cocoa_app
47  *
48  * Transforms the main thread from merely idling in its run loop to
49  * being a Cocoa application running its event loop.
50  *
51  * This will be the perform callback of a custom run loop source that
52  * will be scheduled in the main thread's run loop from a secondary
53  * thread by macdrv_start_cocoa_app.  This function communicates that
54  * it has successfully started the application by changing the condition
55  * of a shared NSConditionLock, passed in via the info parameter.
56  *
57  * This function never returns.  It's the new permanent home of the
58  * main thread.
59  */
60 static void run_cocoa_app(void* info)
61 {
62     struct cocoa_app_startup_info* startup_info = info;
63     NSConditionLock* lock = startup_info->lock;
64
65     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
66
67     [WineApplication sharedApplication];
68     [NSApp setDelegate:(WineApplication*)NSApp];
69     [NSApp computeEventTimeAdjustmentFromTicks:startup_info->tickcount uptime:startup_info->uptime_ns];
70
71     /* Retain the lock while we're using it, so macdrv_start_cocoa_app()
72        doesn't deallocate it in the middle of us unlocking it. */
73     [lock retain];
74     [lock lock];
75     [lock unlockWithCondition:COCOA_APP_RUNNING];
76     [lock release];
77
78     [pool release];
79
80     /* Never returns */
81     [NSApp run];
82 }
83
84
85 /***********************************************************************
86  *              macdrv_start_cocoa_app
87  *
88  * Tells the main thread to transform itself into a Cocoa application.
89  *
90  * Returns 0 on success, non-zero on failure.
91  */
92 int macdrv_start_cocoa_app(unsigned long long tickcount)
93 {
94     int ret = -1;
95     CFRunLoopSourceRef source;
96     struct cocoa_app_startup_info startup_info;
97     uint64_t uptime_mach = mach_absolute_time();
98     mach_timebase_info_data_t mach_timebase;
99     NSDate* timeLimit;
100     CFRunLoopSourceContext source_context = { 0 };
101
102     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
103
104     /* Make sure Cocoa is in multi-threading mode by detaching a
105        do-nothing thread. */
106     [NSThread detachNewThreadSelector:@selector(self)
107                              toTarget:[NSThread class]
108                            withObject:nil];
109
110     startup_info.lock = [[NSConditionLock alloc] initWithCondition:COCOA_APP_NOT_RUNNING];
111     startup_info.tickcount = tickcount;
112
113     mach_timebase_info(&mach_timebase);
114     startup_info.uptime_ns = uptime_mach * mach_timebase.numer / mach_timebase.denom;
115
116     timeLimit = [NSDate dateWithTimeIntervalSinceNow:5];
117
118     source_context.info = &startup_info;
119     source_context.perform = run_cocoa_app;
120     source = CFRunLoopSourceCreate(NULL, 0, &source_context);
121
122     if (source && startup_info.lock && timeLimit)
123     {
124         CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopCommonModes);
125         CFRunLoopSourceSignal(source);
126         CFRunLoopWakeUp(CFRunLoopGetMain());
127
128         if ([startup_info.lock lockWhenCondition:COCOA_APP_RUNNING beforeDate:timeLimit])
129         {
130             [startup_info.lock unlock];
131             ret = 0;
132         }
133     }
134
135     if (source)
136         CFRelease(source);
137     [startup_info.lock release];
138     [pool release];
139     return ret;
140 }