1 /*
2 
3   Copyright (c) 2003-2013 uim Project https://github.com/uim/uim
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 #ifdef HAVE_CONFIG_H
34 # include <config.h>
35 #endif
36 
37 #include <cerrno>
38 #include <cstdio>
39 #include <cstdlib>
40 #include <fcntl.h>
41 #include <unistd.h>
42 #include <sys/types.h>
43 #include <sys/param.h>
44 
45 #include "uim/uim.h"
46 #include "uim/uim-util.h"
47 #include "uim/uim-scm.h"
48 
49 #include "ximserver.h"
50 #include "xim.h"
51 #include "convdisp.h"
52 #include "canddisp.h"
53 #include "util.h"
54 
55 #define CANDWIN_PROG_PREFIX	(UIM_LIBEXECDIR "/uim-candwin")
56 #if defined(USE_QT_CANDWIN)
57   #define CANDWIN_PROG_SUFFIX	"-qt"
58 #elif defined(USE_QT4_CANDWIN)
59   #define CANDWIN_PROG_SUFFIX	"-qt4"
60 #elif defined(USE_GTK_CANDWIN) && defined(USE_GTK2)
61   #define CANDWIN_PROG_SUFFIX	"-gtk"
62 #elif defined(USE_GTK3_CANDWIN)
63   #define CANDWIN_PROG_SUFFIX	"-gtk3"
64 #else
65   #define NO_TOOLKIT
66 #endif
67 
68 static FILE *candwin_r, *candwin_w;
69 static int candwin_pid;
70 static Canddisp *disp;
71 static const char *command;
72 static bool candwin_initted = false;
73 
74 static void candwin_read_cb(int fd, int ev);
75 
candwin_command(void)76 static const char *candwin_command(void)
77 {
78     static char candwin_prog[MAXPATHLEN];
79     const char *user_config;
80     char *str, *style;
81 
82 #ifdef NO_TOOLKIT
83     return NULL;
84 #else
85     /*
86       Search order of candwin_command be summarized as follows
87 	 1. UIM_CANDWIN_PROG -- mainly for debugging purpose
88 	 2. value in 'uim-candwin-prog' symbol (deprecated)
89 	 3. default toolkit's candwin program determined by ./configure
90 	    and the style is selected from 'candidate-window-style' symbol
91      */
92 
93     user_config = getenv("UIM_CANDWIN_PROG");
94     str = user_config ?  strdup(user_config) :
95 	    uim_scm_symbol_value_str("uim-candwin-prog");
96 
97     if (str && *str) {
98 	snprintf(candwin_prog, MAXPATHLEN, UIM_LIBEXECDIR "/%s", str);
99 	free(str);
100 
101 	return candwin_prog;
102     }
103     free(str);
104 
105 #define TYPELEN	20
106     style = uim_scm_symbol_value_str("candidate-window-style");
107     char type[TYPELEN] = "";
108     if (style) {
109 	if (!strcmp(style, "table"))
110 	    strlcpy(type, "-tbl", TYPELEN);
111 	else if (!strcmp(style, "horizontal"))
112 	    strlcpy(type, "-horizontal", TYPELEN);
113     }
114     snprintf(candwin_prog, MAXPATHLEN, "%s%s%s", CANDWIN_PROG_PREFIX, type, CANDWIN_PROG_SUFFIX);
115 
116     return candwin_prog;
117 #endif
118 }
119 
canddisp_singleton()120 Canddisp *canddisp_singleton()
121 {
122     if (XimServer::gCandWinStyleUpdated) {
123 	terminate_canddisp_connection();
124 	command = NULL;
125 	XimServer::gCandWinStyleUpdated = false;
126     }
127 
128     if (!command)
129 	command = candwin_command();
130 
131     if (!candwin_initted && command) {
132 	candwin_pid = uim_ipc_open_command(candwin_pid, &candwin_r, &candwin_w, command);
133 	if (disp)
134 	    delete disp;
135 	disp = new Canddisp();
136 	int fd = fileno(candwin_r);
137 	if (fd != -1) {
138 	    int flag = fcntl(fd, F_GETFL);
139 	    if (flag != -1) {
140 		flag |= O_NONBLOCK;
141 		if (fcntl(fd, F_SETFL, flag) != -1)
142 		    add_fd_watch(fd, READ_OK, candwin_read_cb);
143 	    }
144 	}
145 	candwin_initted = true;
146     }
147     return disp;
148 }
149 
Canddisp()150 Canddisp::Canddisp()
151 {
152 }
153 
~Canddisp()154 Canddisp::~Canddisp() {
155 }
156 
activate(std::vector<const char * > candidates,int display_limit)157 void Canddisp::activate(std::vector<const char *> candidates, int display_limit)
158 {
159     std::vector<const char *>::iterator i;
160 
161     if (!candwin_w)
162 	return;
163 
164     fprintf(candwin_w, "activate\fcharset=UTF-8\fdisplay_limit=%d\f",
165 		    display_limit);
166     for (i = candidates.begin(); i != candidates.end(); ++i)
167 	fprintf(candwin_w, "%s\f", *i);
168     fprintf(candwin_w, "\f");
169     fflush(candwin_w);
170     check_connection();
171 }
172 
173 #if UIM_XIM_USE_NEW_PAGE_HANDLING
set_nr_candidates(int nr,int display_limit)174 void Canddisp::set_nr_candidates(int nr, int display_limit)
175 {
176     if (!candwin_w)
177 	return;
178 
179     fprintf(candwin_w, "set_nr_candidates\f");
180     fprintf(candwin_w, "%d\f", nr);
181     fprintf(candwin_w, "%d\f", display_limit);
182     fprintf(candwin_w, "\f");
183     fflush(candwin_w);
184     check_connection();
185 }
186 
set_page_candidates(int page,CandList candidates)187 void Canddisp::set_page_candidates(int page, CandList candidates)
188 {
189     std::vector<const char *>::iterator i;
190 
191     if (!candwin_w)
192 	return;
193 
194     fprintf(candwin_w, "set_page_candidates\fcharset=UTF-8\fpage=%d\f", page);
195     for (i = candidates.begin(); i != candidates.end(); ++i)
196 	fprintf(candwin_w, "%s\f", *i);
197     fprintf(candwin_w, "\f");
198     fflush(candwin_w);
199     check_connection();
200 }
201 
show_page(int page)202 void Canddisp::show_page(int page)
203 {
204     if (!candwin_w)
205 	return;
206 
207     fprintf(candwin_w, "show_page\f");
208     fprintf(candwin_w, "%d\f", page);
209     fprintf(candwin_w, "\f");
210     fflush(candwin_w);
211     check_connection();
212 }
213 #endif /* UIM_XIM_USE_NEW_PAGE_HANDLING */
214 
select(int index,bool need_hilite)215 void Canddisp::select(int index, bool need_hilite)
216 {
217     if (!candwin_w)
218 	return;
219     fprintf(candwin_w, "select\f");
220     fprintf(candwin_w, "%d\f", index);
221     fprintf(candwin_w, "%d\f\f", need_hilite? 1 : 0);
222     fflush(candwin_w);
223     check_connection();
224 }
225 
deactivate()226 void Canddisp::deactivate()
227 {
228     if (!candwin_w)
229 	return;
230     fprintf(candwin_w, "deactivate\f\f");
231     fflush(candwin_w);
232     check_connection();
233 }
234 
show()235 void Canddisp::show()
236 {
237     if (!candwin_w)
238 	return;
239     fprintf(candwin_w, "show\f\f");
240     fflush(candwin_w);
241     check_connection();
242 }
243 
hide()244 void Canddisp::hide()
245 {
246     if (!candwin_w)
247 	return;
248     fprintf(candwin_w, "hide\f\f");
249     fflush(candwin_w);
250     check_connection();
251 }
252 
move(int x,int y)253 void Canddisp::move(int x, int y)
254 {
255     if (!candwin_w)
256 	return;
257     fprintf(candwin_w, "move\f");
258     fprintf(candwin_w, "%d\f", x);
259     fprintf(candwin_w, "%d\f", y);
260     fprintf(candwin_w, "\f");
261     fflush(candwin_w);
262     check_connection();
263 }
264 
show_caret_state(const char * str,int timeout)265 void Canddisp::show_caret_state(const char *str, int timeout)
266 {
267     if (!candwin_w)
268 	return;
269     fprintf(candwin_w, "show_caret_state\f");
270     fprintf(candwin_w, "%d\f", timeout);
271     fprintf(candwin_w, "%s\f", str);
272     fprintf(candwin_w, "\f");
273     fflush(candwin_w);
274     check_connection();
275 }
276 
update_caret_state()277 void Canddisp::update_caret_state()
278 {
279     if (!candwin_w)
280 	return;
281     fprintf(candwin_w, "update_caret_state\f\f");
282     fflush(candwin_w);
283     check_connection();
284 }
285 
hide_caret_state()286 void Canddisp::hide_caret_state()
287 {
288     if (!candwin_w)
289 	return;
290     fprintf(candwin_w, "hide_caret_state\f\f");
291     fflush(candwin_w);
292     check_connection();
293 }
294 
check_connection()295 void Canddisp::check_connection()
296 {
297     if (errno == EBADF || errno == EPIPE)
298 	terminate_canddisp_connection();
299 }
300 
candwin_read_cb(int fd,int)301 static void candwin_read_cb(int fd, int /* ev */)
302 {
303     char buf[1024];
304     int n;
305 
306     n = static_cast<int>(read(fd, buf, 1024 - 1));
307     if (n == 0) {
308 	terminate_canddisp_connection();
309 	return;
310     }
311     if (n == -1)
312 	return;
313     buf[n] = '\0';
314 
315     if (!strcmp(buf, "err")) {
316 	terminate_canddisp_connection();
317 	return;
318     }
319 
320     InputContext *focusedContext = InputContext::focusedContext();
321     if (focusedContext) {
322 	char *line = buf;
323 	char *eol = strchr(line, '\n');
324 	if (eol != NULL)
325 	    *eol = '\0';
326 
327 	if (strcmp("index", line) == 0) {
328 	    line = eol + 1;
329 	    eol = strchr(line, '\n');
330 	    if (eol != NULL)
331 		*eol = '\0';
332 
333 	    int index;
334 	    sscanf(line, "%d", &index);
335 	    focusedContext->candidate_select(index);
336 	    uim_set_candidate_index(focusedContext->getUC(), index);
337 	    // send packet queue for drawing on-the-spot preedit strings
338 	    focusedContext->get_ic()->force_send_packet();
339 	}
340     }
341     return;
342 }
343 
terminate_canddisp_connection()344 void terminate_canddisp_connection()
345 {
346     int fd_r, fd_w;
347 
348     if (candwin_r) {
349 	fd_r = fileno(candwin_r);
350 	close(fd_r);
351 	remove_current_fd_watch(fd_r);
352     }
353     if (candwin_w) {
354 	fd_w = fileno(candwin_w);
355 	close(fd_w);
356     }
357 
358     candwin_w = candwin_r = NULL;
359     candwin_initted = false;
360     return;
361 }
362