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