1/*
2
3  Copyright (c) 2009 MacUIM Project http://code.google.com/p/macuim/
4
5  All rights reserved.
6
7  Redistribution and use in source and binary forms, with or without
8  modification, are permitted provided that the following conditions
9  are met:
10
11  1. Redistributions of source code must retain the above copyright
12     notice, this list of conditions and the following disclaimer.
13  2. Redistributions in binary form must reproduce the above copyright
14     notice, this list of conditions and the following disclaimer in the
15     documentation and/or other materials provided with the distribution.
16  3. Neither the name of authors nor the names of its contributors
17     may be used to endorse or promote products derived from this software
18     without specific prior written permission.
19
20  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
21  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
24  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  SUCH DAMAGE.
31
32*/
33
34#import "MacUIMController.h"
35#import "UimHelperController.h"
36
37static UimHelperController *sharedController;
38
39static void helperRead(CFSocketRef sock, CFSocketCallBackType callbackType,
40		       CFDataRef address, const void *data, void *info)
41{
42	[sharedController helperRead:(CFSocketRef)sock];
43}
44
45static void helperDisconnect()
46{
47	[sharedController helperDisconnect];
48}
49
50@implementation UimHelperController
51
52+ (id)sharedController
53{
54	if (!sharedController)
55		[[self alloc] init];
56
57	return sharedController;
58}
59
60- (id)init
61{
62	if (sharedController)
63		return sharedController;
64
65	self = [super init];
66	sharedController = self;
67
68	uimRun = NULL;
69	uimSock = NULL;
70	uimFD = -1;
71	isMacUIMfocused = YES;
72
73	return self;
74}
75
76- (void)dealloc
77{
78	sharedController = nil;
79	[super dealloc];
80}
81
82- (void)checkHelperConnection
83{
84	if (uimFD != -1)
85		return; /* already connected */
86
87	uimFD = uim_helper_init_client_fd(helperDisconnect);
88	if (uimFD == -1)
89		return;
90
91	if (!uimSock) {
92		CFSocketContext sockContext;
93
94		sockContext.version = 0;
95		sockContext.info = NULL;
96		sockContext.retain = NULL;
97		sockContext.release = NULL;
98		sockContext.copyDescription = NULL;
99
100		uimSock = CFSocketCreateWithNative(kCFAllocatorDefault,
101			  uimFD,
102			  kCFSocketReadCallBack,
103			  helperRead,
104			  &sockContext);
105
106		if (!uimSock)
107			return;
108
109	}
110
111	if (!uimRun) {
112		uimRun = CFSocketCreateRunLoopSource(kCFAllocatorDefault,
113						     uimSock, 0);
114		if (!uimRun) {
115			CFRelease(uimSock);
116			uimSock = NULL;
117			return;
118		}
119		CFRunLoopAddSource(CFRunLoopGetCurrent(), uimRun,
120				   kCFRunLoopDefaultMode);
121	}
122}
123
124- (void)helperRead:(CFSocketRef)sock;
125{
126	char *msg;
127
128	uim_helper_read_proc(CFSocketGetNative(sock));
129	while ((msg = uim_helper_get_message())) {
130		[self parseHelperString:msg];
131		free(msg);
132	}
133}
134
135- (void)parseHelperString:(const char *)str
136{
137	MacUIMController *activeContext;
138
139	activeContext = [MacUIMController activeContext];
140
141	if (strncmp("im_change", str, 9) == 0) {
142		[self parseIMChangeString:str];
143  	} else if (strncmp("prop_update_custom", str, 18) == 0) {
144		CFMutableStringRef cfstr;
145		CFArrayRef array;
146		CFIndex count;
147
148		cfstr = CFStringCreateMutable(NULL, 0);
149		CFStringAppendCString(cfstr, str, kCFStringEncodingUTF8);
150
151		array = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, cfstr, CFSTR("\n"));
152
153		if (array && (count = CFArrayGetCount(array)) >= 3) {
154			CFStringRef second = CFArrayGetValueAtIndex(array, 1);
155			CFStringRef third = CFArrayGetValueAtIndex(array, 2);
156			if (second && third) {
157				int custom_len_max = CFStringGetMaximumSizeForEncoding(CFStringGetLength(second), kCFStringEncodingUTF8);
158				int val_len_max = CFStringGetMaximumSizeForEncoding(CFStringGetLength(third), kCFStringEncodingUTF8);
159				char custom[custom_len_max];
160				char val[val_len_max];
161
162				CFStringGetCString(second, custom,
163						   custom_len_max,
164						   kCFStringEncodingUTF8);
165				CFStringGetCString(third, val,
166						   val_len_max,
167						   kCFStringEncodingUTF8);
168				[MacUIMController updateCustom:custom:val];
169			}
170		}
171		CFRelease(array);
172		CFRelease(cfstr);
173	} else if (strncmp("custom_reload_notify", str, 20) == 0) {
174		uim_prop_reload_configs();
175	} else if (isMacUIMfocused) {
176		if (strncmp("prop_list_get", str, 13) == 0) {
177			uim_prop_list_update([activeContext uc]);
178		} else if (strncmp("prop_activate", str, 13) == 0) {
179			CFMutableStringRef cfstr;
180			CFArrayRef array;
181
182			cfstr = CFStringCreateMutable(NULL, 0);
183			CFStringAppendCString(cfstr, str, kCFStringEncodingUTF8);
184			//NSLog(@"prop_activate: %@", (NSString *)cfstr);
185
186			array = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, cfstr, CFSTR("\n"));
187
188			if (array && (CFArrayGetCount(array) >= 2)) {
189				CFStringRef second;
190				second = CFArrayGetValueAtIndex(array, 1);
191				if (second) {
192					int max = CFStringGetMaximumSizeForEncoding(CFStringGetLength(second), kCFStringEncodingUTF8);
193					char how[max];
194					CFStringGetCString(second, how, max,
195							   kCFStringEncodingUTF8);
196				        //BlockUpdatePreedit();
197					//NSLog(@"call uim_prop_activate %@", (NSString *)second);
198					uim_prop_activate([activeContext uc], how);
199					//UnblockUpdatePreedit();
200				}
201			}
202			CFRelease(array);
203			CFRelease(cfstr);
204  		} else if (strncmp("focus_in", str, 8) == 0) {
205			isMacUIMfocused = NO;
206		}
207	}
208}
209
210// just do whole IM switch in MacUIM
211- (void)parseIMChangeString:(const char *)str
212{
213	char *eol, *im;
214
215	eol = strchr(str, '\n');
216	if (eol == NULL)
217		return;
218	im = eol + 1;
219	eol = strchr(im, '\n');
220	if (eol == NULL)
221		return;
222	*eol = '\0';
223
224	[MacUIMController switchIM:im];
225}
226
227- (void)helperDisconnect
228{
229	NSLog(@"helperDisconnect");
230	CFRunLoopRemoveSource(CFRunLoopGetCurrent(), uimRun,
231			kCFRunLoopDefaultMode);
232	CFRelease(uimRun);
233	CFRelease(uimSock);
234
235	uimRun = NULL;
236	uimSock = NULL;
237	uimFD = -1;
238}
239
240- (void)focusIn:(uim_context)uc
241{
242	uim_helper_client_focus_in(uc);
243	isMacUIMfocused = YES;
244}
245
246- (void)focusOut:(uim_context)uc
247{
248	uim_helper_client_focus_out(uc);
249}
250
251- (void)send:(const char *)string
252{
253	uim_helper_send_message(uimFD, string);
254}
255
256@end
257