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 // connection management
34
35 #ifdef HAVE_CONFIG_H
36 # include <config.h>
37 #endif
38
39 #include "connection.h"
40
41 #include <csignal>
42 #include <cstdio>
43 #include <cstdlib>
44 #include <list>
45 #include <map>
46 #include <unistd.h>
47 #include <X11/Xatom.h>
48 #ifdef HAVE_ALLOCA_H
49 # include <alloca.h>
50 #endif
51
52 #define INIT_BUF_SIZE 1024
53 #define TRANSPORT_UNIT 20
54 #define TRANSPORT_MAX 20 // Emacs's XIM won't work correctly when the value is 100
55
56 #ifndef HAVE_SIG_T
57 typedef void (*sig_t)(int);
58 #endif
59
60 extern char *xim_packet_name[];
61
62 static std::map<Window, XConnection *> gXConnections;
63 static Atom xim_xconnect;
64 static Atom xim_protocol;
65 static Atom xim_moredata;
66 static Atom uim_comm;
67
68 void
setupNewConnection(XClientMessageEvent * ev)69 XimServer::setupNewConnection(XClientMessageEvent *ev)
70 {
71 XClientMessageEvent r;
72 Window comWin, clientWin;
73
74 comWin = XCreateSimpleWindow(XimServer::gDpy, ev->window,
75 0, 0, 1, 1, 0, 0, 0);
76 clientWin = ev->data.l[0];
77
78 r.type = ClientMessage;
79 r.window = clientWin;
80 r.message_type = xim_xconnect;
81 r.format = 32;
82 r.data.l[0] = comWin;
83 r.data.l[1] = 0;
84 r.data.l[2] = 2;
85 r.data.l[3] = TRANSPORT_MAX;
86 XSendEvent(XimServer::gDpy, clientWin, False, NoEventMask, (XEvent *)&r);
87
88 XConnection *xc = new XConnection(this, clientWin, comWin);
89 std::pair<Window, XConnection *> p(comWin, xc);
90 gXConnections.insert(p);
91 }
92
93 static void
reapOldConnection()94 reapOldConnection()
95 {
96 std::map<Window, XConnection *>::iterator it;
97 for (it = gXConnections.begin(); it != gXConnections.end(); ++it) {
98 XConnection *xc = (*it).second;
99 if (!xc->isValid()) {
100 delete xc;
101 gXConnections.erase(it);
102 return;
103 }
104 }
105 }
106
107 void
procXClientMessage(XClientMessageEvent * ev)108 procXClientMessage(XClientMessageEvent *ev)
109 {
110 // check connection request
111 XimServer *xs = XimServer::findServer(ev->window);
112 if (xs && ev->message_type == xim_xconnect) {
113 xs->setupNewConnection(ev);
114 reapOldConnection();
115 return;
116 }
117
118 // process xim packet
119 if (ev->message_type != xim_moredata &&
120 ev->message_type != xim_protocol) {
121 printf("non xim message\n");
122 return;
123 }
124 std::map<Window, XConnection *>::iterator i;
125 i = gXConnections.find(ev->window);
126 if (i == gXConnections.end()) {
127 printf("packet for unknown window\n");
128 return;
129 }
130
131 XConnection *xc = (*i).second;
132 xc->readProc(ev);
133 }
134
XConnection(XimServer * svr,Window clientWin,Window commWin)135 XConnection::XConnection(XimServer *svr, Window clientWin, Window commWin) :
136 Connection(svr)
137 {
138 mClientWin = clientWin;
139 mCommWin = commWin;
140 mBuf.buf = (char *)malloc(INIT_BUF_SIZE);
141 mBuf.len = 0;
142 mBuf.size = INIT_BUF_SIZE;
143 add_window_watch(mClientWin, this, STRUCTURE_NOTIFY_MASK);
144 mIsValid = true;
145 }
146
~XConnection()147 XConnection::~XConnection()
148 {
149 if (mIsValid)
150 remove_window_watch(mClientWin);
151 XDestroyWindow(XimServer::gDpy, mCommWin);
152 free(mBuf.buf);
153 }
154
destroy(Window)155 void XConnection::destroy(Window /* w */)
156 {
157 if (mIsValid)
158 OnClose();
159 mIsValid = false;
160 }
161
readProc(XClientMessageEvent * ev)162 void XConnection::readProc(XClientMessageEvent *ev)
163 {
164 if (!readToBuf(ev))
165 return;
166
167 checkByteorder();
168
169 bool pushed = true;
170 do {
171 int len = -1;
172 if (mBuf.len >= 4)
173 len = RxPacket::getPacketLength((unsigned char *)mBuf.buf,
174 mByteorder);
175
176 if ((len > 4 && len <= mBuf.len) ||
177 (len == 4 && mBuf.buf[0] == XIM_DISCONNECT)) {
178 RxPacket *p =
179 createRxPacket((unsigned char *)mBuf.buf, mByteorder);
180 shiftBuffer(len);
181 mRxQ.push_back(p);
182 } else if (len == 4) {
183 // do nothing
184 shiftBuffer(4);
185 } else {
186 pushed = false;
187 }
188 } while (pushed);
189 OnRecv();
190
191 writeProc();
192 }
193
shiftBuffer(int len)194 void XConnection::shiftBuffer(int len)
195 {
196 memmove(mBuf.buf, &mBuf.buf[len], mBuf.len - len);
197 mBuf.len -= len;
198 }
199
checkByteorder()200 bool XConnection::checkByteorder()
201 {
202 if (mByteorder == BYTEORDER_UNKNOWN) {
203 if (mBuf.buf[0] != XIM_CONNECT) {
204 printf("not XIM_CONNECT\n");
205 return false;
206 }
207 if (mBuf.buf[4] == 0x42)
208 mByteorder = MSB_FIRST;
209 else if (mBuf.buf[4] == 0x6c)
210 mByteorder = LSB_FIRST;
211 else
212 return false;
213 }
214 return true;
215 }
216
readToBuf(XClientMessageEvent * ev)217 bool XConnection::readToBuf(XClientMessageEvent *ev)
218 {
219 if (ev->format == 32 && ev->message_type == xim_protocol) {
220 // indirect
221 int offset = 0;
222 Atom type;
223 int format;
224 unsigned long nrItems;
225 unsigned long remain;
226 char *data;
227
228 while (ev->data.l[0] >= mBuf.size) {
229 mBuf.size += 1024;
230 mBuf.buf = (char *)realloc(mBuf.buf, mBuf.size);
231 }
232 do {
233 XGetWindowProperty(XimServer::gDpy, ev->window, ev->data.l[1],
234 offset, mBuf.size - mBuf.len, True,
235 AnyPropertyType,
236 &type, &format, &nrItems, &remain,
237 (unsigned char **)(uintptr_t)&data);
238 if (!data)
239 return false;
240
241 if (format == 8) {
242 memcpy(&mBuf.buf[mBuf.len], data, nrItems);
243 mBuf.len += static_cast<int>(nrItems);
244 } else
245 return false;
246 XFree(data);
247 } while (remain > 0);
248 } else if (ev->format == 8) {
249 // direct
250 if ((mBuf.len + TRANSPORT_UNIT) >= mBuf.size) {
251 mBuf.size += TRANSPORT_UNIT;
252 mBuf.buf = (char *)realloc(mBuf.buf, mBuf.size);
253 }
254 memcpy(&mBuf.buf[mBuf.len], ev->data.b, TRANSPORT_UNIT);
255 mBuf.len += TRANSPORT_UNIT;
256 } else
257 return false;
258
259 return true;
260 }
261
writePendingPacket()262 void XConnection::writePendingPacket()
263 {
264 std::list<TxPacket *>::iterator i, j;
265 bool sent_preedit_done = false;
266 C8 major;
267
268 while (!mPendingTxQ.empty()) {
269 if (hasSyncFlag() || hasPreeditStartSyncFlag() ||
270 hasPreeditCaretSyncFlag())
271 break;
272
273 i = mPendingTxQ.begin();
274 major = (*i)->get_major();
275
276 switch (major) {
277 case XIM_COMMIT:
278 case XIM_FORWARD_EVENT:
279 setSyncFlag();
280 break;
281 case XIM_PREEDIT_START:
282 setPreeditStartSyncFlag();
283 break;
284 case XIM_PREEDIT_CARET:
285 setPreeditCaretSyncFlag();
286 break;
287 case XIM_PREEDIT_DONE:
288 sent_preedit_done = true;
289 default:
290 break;
291 }
292
293 doSend(*i, true);
294 delete *i;
295 mPendingTxQ.pop_front();
296
297 // XIM_PREEDIT_START/DRAW just after XIM_PREEDIT_DONE maybe
298 // need to wait XIM_COMMIT and its XIM_SYNC_REPLY
299 if (sent_preedit_done == true) {
300 // push first XIM_COMMIT into the head of the queue
301 std::list<TxPacket *> tmp;
302 bool first = true;
303 while (!mPendingTxQ.empty()) {
304 j = mPendingTxQ.begin();
305 major = (*j)->get_major();
306 if (major == XIM_COMMIT && first == true) {
307 tmp.push_front(*j);
308 first = false;
309 } else
310 tmp.push_back(*j);
311
312 mPendingTxQ.pop_front();
313 }
314 mPendingTxQ = tmp;
315 sent_preedit_done = false;
316 }
317 }
318 }
319
writePassivePacket()320 void XConnection::writePassivePacket()
321 {
322 std::list<TxPacket *>::iterator i, j;
323 bool sent_preedit_done = false;
324 C8 major;
325
326 while (!mPTxQ.empty()) {
327 if (!mPendingTxQ.empty())
328 break;
329
330 i = mPTxQ.begin();
331
332 if (hasSyncFlag() || hasPreeditStartSyncFlag() ||
333 hasPreeditCaretSyncFlag()) {
334 mPendingTxQ.push_back(*i);
335 mPTxQ.pop_front();
336 break;
337 }
338
339 major = (*i)->get_major();
340 switch (major) {
341 case XIM_COMMIT:
342 setSyncFlag();
343 break;
344 case XIM_PREEDIT_START:
345 setPreeditStartSyncFlag();
346 break;
347 case XIM_PREEDIT_CARET:
348 setPreeditCaretSyncFlag();
349 break;
350 case XIM_PREEDIT_DONE:
351 sent_preedit_done = true;
352 break;
353 default:
354 break;
355 }
356
357 doSend(*i, true);
358 delete *i;
359 mPTxQ.pop_front();
360
361 // XIM_PREEDIT_START/DRAW just after XIM_PREEDIT_DONE maybe
362 // need to wait XIM_COMMIT and its XIM_SYNC_REPLY
363 if (sent_preedit_done == true) {
364 // push first XIM_COMMIT into the head of passive queue
365 std::list<TxPacket *> tmp;
366 bool first = true;
367 while (!mPTxQ.empty()) {
368 j = mPTxQ.begin();
369 major = (*j)->get_major();
370 if (major == XIM_COMMIT && first == true) {
371 tmp.push_front(*j);
372 first = false;
373 } else
374 tmp.push_back(*j);
375
376 mPTxQ.pop_front();
377 }
378 mPTxQ = tmp;
379 sent_preedit_done = false;
380 }
381 }
382 }
383
writeNormalPacket()384 void XConnection::writeNormalPacket()
385 {
386 std::list<TxPacket *>::iterator i;
387 C8 major;
388
389 while (!mTxQ.empty()) {
390 i = mTxQ.begin();
391 major = (*i)->get_major();
392 if (major == XIM_FORWARD_EVENT) {
393 if (hasSyncFlag()) {
394 // move this packet to pending queue
395 mPendingTxQ.push_back(*i);
396 mTxQ.pop_front();
397 continue;
398 }
399 setSyncFlag();
400 }
401 doSend(*i, false);
402 delete *i;
403 mTxQ.pop_front();
404 }
405 }
406
writeProc()407 void XConnection::writeProc()
408 {
409 OnSend(); // add XIM_COMMIT packet to passive queue
410
411 writePendingPacket();
412 writePassivePacket();
413 writeNormalPacket();
414
415 // interrupt while _XFlushInt() here will cause lock up of the display.
416 sig_t old_sigusr1 = signal(SIGUSR1, SIG_IGN);
417 XFlush(XimServer::gDpy);
418 signal(SIGUSR1, old_sigusr1);
419
420 if (mIsCloseWait) {
421 remove_window_watch(mClientWin);
422 mClientWin = None;
423 mIsValid = false;
424 OnClose();
425 }
426 }
427
doSend(TxPacket * t,bool passive)428 void XConnection::doSend(TxPacket *t, bool passive)
429 {
430 if (g_option_mask & OPT_TRACE_XIM) {
431 if (passive)
432 printf("(->): %s.\n", xim_packet_name[t->get_major()]);
433 else
434 printf("->: %s.\n", xim_packet_name[t->get_major()]);
435 }
436
437 XClientMessageEvent r;
438 int buflen;
439 char *buf;
440
441 buflen = t->get_length();
442 buf = (char *)alloca(buflen);
443 t->write_to_buf((unsigned char *)buf, buflen, mByteorder);
444 if (buflen < TRANSPORT_MAX) {
445 // via event
446 int offset = 0;
447 int length = buflen;
448 while (length > TRANSPORT_UNIT) {
449 r.type = ClientMessage;
450 r.window = mClientWin;
451 r.format = 8;
452 r.message_type = xim_moredata;
453 memcpy(r.data.b, &buf[offset], TRANSPORT_UNIT);
454 XSendEvent(XimServer::gDpy, mClientWin, False, NoEventMask, (XEvent *)&r);
455 offset += TRANSPORT_UNIT;
456 length -= TRANSPORT_UNIT;
457 }
458 r.type = ClientMessage;
459 r.window = mClientWin;
460 r.format = 8;
461 r.message_type = xim_protocol;
462 memset(r.data.b, 0, TRANSPORT_UNIT);
463 memcpy(r.data.b, &buf[offset], length);
464 XSendEvent(XimServer::gDpy, mClientWin, False, NoEventMask, (XEvent *)&r);
465 } else {
466 // via property
467 r.type = ClientMessage;
468 r.window = mClientWin;
469 r.format = 32;
470 r.message_type = xim_protocol;
471 r.data.l[0] = buflen;
472 r.data.l[1] = uim_comm;
473 XChangeProperty(XimServer::gDpy, mClientWin, uim_comm, XA_STRING,
474 8, PropModeAppend, (unsigned char *)buf, buflen);
475 XSendEvent(XimServer::gDpy, mClientWin, False, NoEventMask, (XEvent *)&r);
476 }
477 }
478
connection_setup()479 int connection_setup()
480 {
481 xim_xconnect = XInternAtom(XimServer::gDpy, "_XIM_XCONNECT", False);
482 xim_protocol = XInternAtom(XimServer::gDpy, "_XIM_PROTOCOL", False);
483 xim_moredata = XInternAtom(XimServer::gDpy, "_XIM_MOREDATA", False);
484 uim_comm = XInternAtom(XimServer::gDpy, "UIM_COMM", False);
485 return 0;
486 }
487 /*
488 * Local variables:
489 * c-indent-level: 4
490 * c-basic-offset: 4
491 * End:
492 */
493