/
IPAIM.c
executable file
·170 lines (157 loc) · 7.24 KB
/
IPAIM.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/*
Copyright © 2005-2010 Brian S. Hall
Portions may be Copyright © 2000-2001 Apple Computer, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "IPAIM.h"
#include <unistd.h>
IPAIMSessionHandle gActiveSession;
static OSStatus IPAIMLaunchWithFSSpec(FSSpec *fileSpec);
/************************************************************************************************
* Called via NewTSMDocument the first time our text service component is instantiated.
*
* Initialize our global state (initialize global variables, launch the
* server process). We only initialize global data
* here. All per-session context initialization is handled in IPAIMSessionOpen().
************************************************************************************************/
ComponentResult IPAIMInitialize(ComponentInstance inComponentInstance)
{
ComponentResult result;
gActiveSession = nil;
result = IPAIMLaunchServer();
if (!result) result = IPAInitMessageReceiving();
return result;
}
/************************************************************************************************
* Called via NewTSMDocument. Initialize a new session context. Create a session handle to store all
* of the information pertinent to the session and initialize all its data structures. We do not
* handle initialize global data (data that is shared across sessions); that is taken care of by
* IPAIMInitialize().
************************************************************************************************/
ComponentResult IPAIMSessionOpen(ComponentInstance inComponentInstance,
IPAIMSessionHandle *outSessionHandle)
{
ComponentResult result = noErr;
IPAIMSessionHandle h = *outSessionHandle;
// If per-session storage is not set up yet, do so now.
if (h == nil) h = (IPAIMSessionHandle)NewHandleClear(sizeof(IPAIMSessionRecord));
if (h) (*h)->_comp = inComponentInstance;
else result = memFullErr;
*outSessionHandle = h;
return result;
}
/************************************************************************************************
* Checks to see if the server is running. If not, it is launched. Control does not return from
* this function until the launch is complete.
************************************************************************************************/
OSStatus IPAIMLaunchServer(void)
{
OSStatus result = noErr;
CFMessagePortRef serverPortRef;
// Obtain a reference to the server port. If it exists, then the server is already running.
serverPortRef = CFMessagePortCreateRemote(NULL, CFSTR(kIPAServerPortName));
if (!serverPortRef)
{
// The server is not running, so launch it now.
// Obtain a reference to our text service component bundle. Can't use CFBundleGetMainBundle
// because we are running inside another application's context so we find our bundle using the
// bundle identifier in our Info.plist file.
CFURLRef sharedSupportURL = NULL;
CFURLRef serverURL = NULL;
FSRef serverFSRef;
FSSpec serverFileSpec;
CFBundleRef myComponentBundle = CFBundleGetBundleWithIdentifier(kBundleIdentifier);
// If we got a reference to the bundle, locate the "SharedSupport" directory inside the bundle.
if (myComponentBundle) sharedSupportURL = CFBundleCopySharedSupportURL(myComponentBundle);
// If we found the "SharedSupport" directory, append the name of our server application to the
// URL so we can identify it.
if (sharedSupportURL)
serverURL = CFURLCreateCopyAppendingPathComponent(nil, sharedSupportURL,
kServerName, false);
// We need to do some extra work here. Since LaunchApplication only takes an FSSpec as a parameter,
// me must convert the server URL into an FSRef and then convert it into an FSSpec. Whew!
if (!serverURL) result = -2;
else
{
if (!CFURLGetFSRef(serverURL, &serverFSRef)) result = -2;
else
{
result = FSGetCatalogInfo(&serverFSRef, kFSCatInfoNone, nil, nil, &serverFileSpec, nil);
if (result == noErr)
{
// Wait for the server to come up (timeout in 20 seconds -- this is arbitrary and may need
// adjusting for certain situations such as launching over a network)
long timeout = TickCount() + 1200;
// Launch the server application.
result = IPAIMLaunchWithFSSpec(&serverFileSpec);
while (result == noErr)
{
struct timespec ts = {0,90000000};
// Get a reference to the server port.
serverPortRef = CFMessagePortCreateRemote(nil, CFSTR(kIPAServerPortName));
if (serverPortRef) break;
if (TickCount() > timeout)
{
result = -1;
break;
}
// Sleep a little to give time to the server.
// Don't just keep pounding on it.
(void)nanosleep(&ts, NULL);
}
}
}
CFRelease(serverURL);
}
if (sharedSupportURL) CFRelease(sharedSupportURL);
if (result == -1) IPALog("IPAIMLaunchServer: timeout occured while trying to launch UI server.");
else if (result == -2) IPALog("IPAIMLaunchServer: unable to locate the Shared Support directory.");
else if (result) IPALog("IPAIMLaunchServer: error %ld occured while trying to launch the UI.", result);
}
if (serverPortRef) CFRelease(serverPortRef);
return result;
}
static LaunchParamBlockRec lpbr =
{0,0,extendedBlock,extendedBlockLen, //reserved1,reserved2,launchBlockID,launchEPBLength
0,launchNoFileFlags + launchContinue + launchDontSwitch, //launchFileFlags, launchControlFlags
NULL, {0,0}, //launchAppSpec, launchProcessSN
0, 0, 0, NULL}; //launchPreferredSize, launchMinimumSize, launchAvailableSize, launchAppParameters
static OSStatus IPAIMLaunchWithFSSpec(FSSpec* fileSpec)
{
lpbr.launchAppSpec = fileSpec;
return LaunchApplication(&lpbr);
}
// A logger that gives us info about the process id and process name.
void IPALog(char* fmt, ...)
{
va_list ap;
ProcessSerialNumber psn = {0, kCurrentProcess};
CFStringRef myName = nil;
CFDataRef utf8 = nil;
OSErr err = CopyProcessName(&psn, &myName);
ProcessInfoRec pir;
pir.processInfoLength = sizeof(pir);
pir.processName = nil;
pir.processAppSpec = nil;
(void)GetProcessInformation(&psn, &pir);
if (!err) utf8 = CFStringCreateExternalRepresentation(NULL, myName, kCFStringEncodingUTF8, 0);
if (utf8) printf("IPA Palette (%d:[%ld,%ld]:%.*s): ", getpid(),
pir.processNumber.highLongOfPSN, pir.processNumber.lowLongOfPSN,
(int)CFDataGetLength(utf8), CFDataGetBytePtr(utf8));
else printf("IPA Palette (%d): ", getpid());
if (myName) CFRelease(myName);
if (utf8) CFRelease(utf8);
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
printf("\n");
fflush(stdout);
}