Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*************************************************************************
3 : : *
4 : : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : : *
6 : : * Copyright 2000, 2010 Oracle and/or its affiliates.
7 : : *
8 : : * OpenOffice.org - a multi-platform office productivity suite
9 : : *
10 : : * This file is part of OpenOffice.org.
11 : : *
12 : : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU Lesser General Public License version 3
14 : : * only, as published by the Free Software Foundation.
15 : : *
16 : : * OpenOffice.org is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : : * GNU Lesser General Public License version 3 for more details
20 : : * (a copy is included in the LICENSE file that accompanied this code).
21 : : *
22 : : * You should have received a copy of the GNU Lesser General Public License
23 : : * version 3 along with OpenOffice.org. If not, see
24 : : * <http://www.openoffice.org/license.html>
25 : : * for a copy of the LGPLv3 License.
26 : : *
27 : : ************************************************************************/
28 : :
29 : :
30 : : #include <stdio.h>
31 : : #include <string.h>
32 : :
33 : : #ifdef LINUX
34 : : # ifndef __USE_XOPEN
35 : : # define __USE_XOPEN
36 : : # endif
37 : : #endif
38 : : #include <poll.h>
39 : :
40 : : #include <tools/prex.h>
41 : : #include <X11/Xlocale.h>
42 : : #include <X11/Xlib.h>
43 : : #include <unx/XIM.h>
44 : : #include <tools/postx.h>
45 : :
46 : : #include "unx/salunx.h"
47 : : #include "unx/saldisp.hxx"
48 : : #include "unx/i18n_im.hxx"
49 : : #include "unx/i18n_status.hxx"
50 : :
51 : : #include <osl/thread.h>
52 : : #include <osl/process.h>
53 : :
54 : : using namespace vcl;
55 : : #include "unx/i18n_cb.hxx"
56 : : #if defined(SOLARIS) || defined(LINUX)
57 : : extern "C" char * XSetIMValues(XIM im, ...);
58 : : #endif
59 : :
60 : : // ------------------------------------------------------------------------------------
61 : : //
62 : : // kinput2 IME needs special key handling since key release events are filtered in
63 : : // preeditmode and XmbResetIC does not work
64 : : //
65 : : // ------------------------------------------------------------------------------------
66 : :
67 : : Bool
68 : 0 : IMServerKinput2 ()
69 : : {
70 : 0 : const static char* p_xmodifiers = getenv ("XMODIFIERS");
71 : : const static Bool b_kinput2 = (p_xmodifiers != NULL)
72 : 0 : && (strcmp(p_xmodifiers, "@im=kinput2") == 0);
73 : :
74 : 0 : return b_kinput2;
75 : : }
76 : :
77 : : class XKeyEventOp : XKeyEvent
78 : : {
79 : : private:
80 : : void init();
81 : :
82 : : public:
83 : : XKeyEventOp();
84 : : ~XKeyEventOp();
85 : :
86 : : XKeyEventOp& operator= (const XKeyEvent &rEvent);
87 : : void erase ();
88 : : Bool match (const XKeyEvent &rEvent) const;
89 : : };
90 : :
91 : : void
92 : 0 : XKeyEventOp::init()
93 : : {
94 : 0 : type = 0; /* serial = 0; */
95 : 0 : send_event = 0; display = 0;
96 : 0 : window = 0; root = 0;
97 : 0 : subwindow = 0; /* time = 0; */
98 : : /* x = 0; y = 0; */
99 : : /* x_root = 0; y_root = 0; */
100 : 0 : state = 0; keycode = 0;
101 : 0 : same_screen = 0;
102 : 0 : }
103 : :
104 : 0 : XKeyEventOp::XKeyEventOp()
105 : : {
106 : 0 : init();
107 : 0 : }
108 : :
109 : 0 : XKeyEventOp::~XKeyEventOp()
110 : : {
111 : 0 : }
112 : :
113 : : XKeyEventOp&
114 : 0 : XKeyEventOp::operator= (const XKeyEvent &rEvent)
115 : : {
116 : 0 : type = rEvent.type; /* serial = rEvent.serial; */
117 : 0 : send_event = rEvent.send_event; display = rEvent.display;
118 : 0 : window = rEvent.window; root = rEvent.root;
119 : 0 : subwindow = rEvent.subwindow;/* time = rEvent.time; */
120 : : /* x = rEvent.x, y = rEvent.y; */
121 : : /* x_root = rEvent.x_root, y_root = rEvent.y_root; */
122 : 0 : state = rEvent.state; keycode = rEvent.keycode;
123 : 0 : same_screen = rEvent.same_screen;
124 : :
125 : 0 : return *this;
126 : : }
127 : :
128 : : void
129 : 0 : XKeyEventOp::erase ()
130 : : {
131 : 0 : init();
132 : 0 : }
133 : :
134 : : Bool
135 : 0 : XKeyEventOp::match (const XKeyEvent &rEvent) const
136 : : {
137 : : return ( (type == XLIB_KeyPress && rEvent.type == KeyRelease)
138 : : || (type == KeyRelease && rEvent.type == XLIB_KeyPress ))
139 : : /* && serial == rEvent.serial */
140 : : && send_event == rEvent.send_event
141 : : && display == rEvent.display
142 : : && window == rEvent.window
143 : : && root == rEvent.root
144 : : && subwindow == rEvent.subwindow
145 : : /* && time == rEvent.time
146 : : && x == rEvent.x
147 : : && y == rEvent.y
148 : : && x_root == rEvent.x_root
149 : : && y_root == rEvent.y_root */
150 : : && state == rEvent.state
151 : : && keycode == rEvent.keycode
152 : 0 : && same_screen == rEvent.same_screen;
153 : : }
154 : :
155 : : // -------------------------------------------------------------------------
156 : : //
157 : : // locale handling
158 : : //
159 : : // -------------------------------------------------------------------------
160 : :
161 : : // Locale handling of the operating system layer
162 : :
163 : : static char*
164 : 0 : SetSystemLocale( const char* p_inlocale )
165 : : {
166 : : char *p_outlocale;
167 : :
168 : 0 : if ( (p_outlocale = setlocale(LC_ALL, p_inlocale)) == NULL )
169 : : {
170 : : fprintf( stderr, "I18N: Operating system doesn't support locale \"%s\"\n",
171 : 0 : p_inlocale );
172 : : }
173 : :
174 : 0 : return p_outlocale;
175 : : }
176 : :
177 : : #ifdef SOLARIS
178 : : static void
179 : : SetSystemEnvironment( const rtl::OUString& rLocale )
180 : : {
181 : : rtl::OUString LC_ALL_Var(RTL_CONSTASCII_USTRINGPARAM("LC_ALL"));
182 : : osl_setEnvironment(LC_ALL_Var.pData, rLocale.pData);
183 : :
184 : : rtl::OUString LANG_Var(RTL_CONSTASCII_USTRINGPARAM("LANG"));
185 : : osl_setEnvironment(LANG_Var.pData, rLocale.pData);
186 : : }
187 : : #endif
188 : :
189 : : static Bool
190 : 0 : IsPosixLocale( const char* p_locale )
191 : : {
192 : 0 : if ( p_locale == NULL )
193 : 0 : return False;
194 : 0 : if ( (p_locale[ 0 ] == 'C') && (p_locale[ 1 ] == '\0') )
195 : 0 : return True;
196 : 0 : if ( strncmp(p_locale, "POSIX", sizeof("POSIX")) == 0 )
197 : 0 : return True;
198 : :
199 : 0 : return False;
200 : : }
201 : :
202 : : // Locale handling of the X Window System layer
203 : :
204 : : static Bool
205 : 0 : IsXWindowCompatibleLocale( const char* p_locale )
206 : : {
207 : 0 : if ( p_locale == NULL )
208 : 0 : return False;
209 : :
210 : 0 : if ( !XSupportsLocale() )
211 : : {
212 : : fprintf (stderr, "I18N: X Window System doesn't support locale \"%s\"\n",
213 : 0 : p_locale );
214 : 0 : return False;
215 : : }
216 : 0 : return True;
217 : : }
218 : :
219 : : // Set the operating system locale prior to trying to open an
220 : : // XIM InputMethod.
221 : : // Handle the cases where the current locale is either not supported by the
222 : : // operating system (LANG=gaga) or by the XWindow system (LANG=aa_ER@saaho)
223 : : // by providing a fallback.
224 : : // Upgrade "C" or "POSIX" to "en_US" locale to allow umlauts and accents
225 : : // see i8988, i9188, i8930, i16318
226 : : // on Solaris the environment needs to be set equivalent to the locale (#i37047#)
227 : :
228 : : Bool
229 : 0 : SalI18N_InputMethod::SetLocale( const char* pLocale )
230 : : {
231 : : // check whether we want an Input Method engine, if we don't we
232 : : // do not need to set the locale
233 : 0 : if ( mbUseable )
234 : : {
235 : 0 : char *locale = SetSystemLocale( pLocale );
236 : 0 : if ( (!IsXWindowCompatibleLocale(locale)) || IsPosixLocale(locale) )
237 : : {
238 : 0 : osl_setThreadTextEncoding (RTL_TEXTENCODING_ISO_8859_1);
239 : 0 : locale = SetSystemLocale( "en_US" );
240 : : #ifdef SOLARIS
241 : : SetSystemEnvironment( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("en_US")) );
242 : : #endif
243 : 0 : if (! IsXWindowCompatibleLocale(locale))
244 : : {
245 : 0 : locale = SetSystemLocale( "C" );
246 : : #ifdef SOLARIS
247 : : SetSystemEnvironment( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("C")) );
248 : : #endif
249 : 0 : if (! IsXWindowCompatibleLocale(locale))
250 : 0 : mbUseable = False;
251 : : }
252 : : }
253 : :
254 : : // must not fail if mbUseable since XSupportsLocale() asserts success
255 : 0 : if ( mbUseable && XSetLocaleModifiers("") == NULL )
256 : : {
257 : : fprintf (stderr, "I18N: Can't set X modifiers for locale \"%s\"\n",
258 : 0 : locale);
259 : 0 : mbUseable = False;
260 : : }
261 : : }
262 : :
263 : 0 : return mbUseable;
264 : : }
265 : :
266 : : Bool
267 : 0 : SalI18N_InputMethod::PosixLocale()
268 : : {
269 : 0 : if (maMethod)
270 : 0 : return IsPosixLocale (XLocaleOfIM (maMethod));
271 : 0 : return False;
272 : : }
273 : :
274 : : // ------------------------------------------------------------------------
275 : : //
276 : : // Constructor / Destructor / Initialisation
277 : : //
278 : : // ------------------------------------------------------------------------
279 : :
280 : 0 : SalI18N_InputMethod::SalI18N_InputMethod( ) : mbUseable( bUseInputMethodDefault ),
281 : : maMethod( (XIM)NULL ),
282 : 0 : mpStyles( (XIMStyles*)NULL )
283 : : {
284 : 0 : const char *pUseInputMethod = getenv( "SAL_USEINPUTMETHOD" );
285 : 0 : if ( pUseInputMethod != NULL )
286 : 0 : mbUseable = pUseInputMethod[0] != '\0' ;
287 : 0 : }
288 : :
289 : 0 : SalI18N_InputMethod::~SalI18N_InputMethod()
290 : : {
291 : 0 : ::vcl::I18NStatus::free();
292 : 0 : if ( mpStyles != NULL )
293 : 0 : XFree( mpStyles );
294 : 0 : if ( maMethod != NULL )
295 : 0 : XCloseIM ( maMethod );
296 : 0 : }
297 : :
298 : : //
299 : : // XXX
300 : : // debug routine: lets have a look at the provided method styles
301 : : //
302 : :
303 : : #if OSL_DEBUG_LEVEL > 1
304 : :
305 : : extern "C" char*
306 : : GetMethodName( XIMStyle nStyle, char *pBuf, int nBufSize)
307 : : {
308 : : struct StyleName {
309 : : const XIMStyle nStyle;
310 : : const char *pName;
311 : : const int nNameLen;
312 : : };
313 : :
314 : : StyleName *pDescPtr;
315 : : static const StyleName pDescription[] = {
316 : : { XIMPreeditArea, "PreeditArea ", sizeof("PreeditArea ") },
317 : : { XIMPreeditCallbacks, "PreeditCallbacks ",sizeof("PreeditCallbacks ")},
318 : : { XIMPreeditPosition, "PreeditPosition ", sizeof("PreeditPosition ") },
319 : : { XIMPreeditNothing, "PreeditNothing ", sizeof("PreeditNothing ") },
320 : : { XIMPreeditNone, "PreeditNone ", sizeof("PreeditNone ") },
321 : : { XIMStatusArea, "StatusArea ", sizeof("StatusArea ") },
322 : : { XIMStatusCallbacks, "StatusCallbacks ", sizeof("StatusCallbacks ") },
323 : : { XIMStatusNothing, "StatusNothing ", sizeof("StatusNothing ") },
324 : : { XIMStatusNone, "StatusNone ", sizeof("StatusNone ") },
325 : : { 0, "NULL", 0 }
326 : : };
327 : :
328 : : if ( nBufSize > 0 )
329 : : pBuf[0] = '\0';
330 : :
331 : : char *pBufPtr = pBuf;
332 : : for ( pDescPtr = const_cast<StyleName*>(pDescription); pDescPtr->nStyle != 0; pDescPtr++ )
333 : : {
334 : : int nSize = pDescPtr->nNameLen - 1;
335 : : if ( (nStyle & pDescPtr->nStyle) && (nBufSize > nSize) )
336 : : {
337 : : strncpy( pBufPtr, pDescPtr->pName, nSize + 1);
338 : : pBufPtr += nSize;
339 : : nBufSize -= nSize;
340 : : }
341 : : }
342 : :
343 : : return pBuf;
344 : : }
345 : :
346 : : extern "C" void
347 : : PrintInputStyle( XIMStyles *pStyle )
348 : : {
349 : : char pBuf[ 128 ];
350 : : int nBuf = sizeof( pBuf );
351 : :
352 : : if ( pStyle == NULL )
353 : : fprintf( stderr, "no input method styles\n");
354 : : else
355 : : for ( int nStyle = 0; nStyle < pStyle->count_styles; nStyle++ )
356 : : {
357 : : fprintf( stderr, "style #%i = %s\n", nStyle,
358 : : GetMethodName(pStyle->supported_styles[nStyle], pBuf, nBuf) );
359 : : }
360 : : }
361 : :
362 : : #endif
363 : :
364 : : //
365 : : // this is the real constructing routine, since locale setting has to be done
366 : : // prior to xopendisplay, the xopenim call has to be delayed
367 : : //
368 : :
369 : : Bool
370 : 0 : SalI18N_InputMethod::CreateMethod ( Display *pDisplay )
371 : : {
372 : 0 : if ( mbUseable )
373 : : {
374 : 0 : maMethod = XOpenIM(pDisplay, NULL, NULL, NULL);
375 : :
376 : 0 : if ((maMethod == (XIM)NULL) && (getenv("XMODIFIERS") != NULL))
377 : : {
378 : 0 : rtl::OUString envVar(RTL_CONSTASCII_USTRINGPARAM("XMODIFIERS"));
379 : 0 : osl_clearEnvironment(envVar.pData);
380 : 0 : XSetLocaleModifiers("");
381 : 0 : maMethod = XOpenIM(pDisplay, NULL, NULL, NULL);
382 : : }
383 : :
384 : 0 : if ( maMethod != (XIM)NULL )
385 : : {
386 : 0 : if ( XGetIMValues(maMethod, XNQueryInputStyle, &mpStyles, NULL)
387 : : != NULL)
388 : 0 : mbUseable = False;
389 : : #if OSL_DEBUG_LEVEL > 1
390 : : fprintf(stderr, "Creating Mono-Lingual InputMethod\n" );
391 : : PrintInputStyle( mpStyles );
392 : : #endif
393 : : }
394 : : else
395 : : {
396 : 0 : mbUseable = False;
397 : : }
398 : : }
399 : :
400 : : #if OSL_DEBUG_LEVEL > 1
401 : : if ( !mbUseable )
402 : : fprintf(stderr, "input method creation failed\n");
403 : : #endif
404 : :
405 : 0 : maDestroyCallback.callback = (XIMProc)IM_IMDestroyCallback;
406 : 0 : maDestroyCallback.client_data = (XPointer)this;
407 : 0 : if (mbUseable && maMethod != NULL)
408 : 0 : XSetIMValues(maMethod, XNDestroyCallback, &maDestroyCallback, NULL);
409 : :
410 : 0 : return mbUseable;
411 : : }
412 : :
413 : : //
414 : : // give IM the opportunity to look at the event, and possibly hide it
415 : : //
416 : :
417 : : Bool
418 : 0 : SalI18N_InputMethod::FilterEvent( XEvent *pEvent, XLIB_Window window )
419 : : {
420 : 0 : if (!mbUseable)
421 : 0 : return False;
422 : :
423 : 0 : Bool bFilterEvent = XFilterEvent (pEvent, window);
424 : :
425 : 0 : if (pEvent->type != XLIB_KeyPress && pEvent->type != KeyRelease)
426 : 0 : return bFilterEvent;
427 : :
428 : : /*
429 : : * fix broken key release handling of some IMs
430 : : */
431 : 0 : XKeyEvent* pKeyEvent = &(pEvent->xkey);
432 : 0 : static XKeyEventOp maLastKeyPress;
433 : :
434 : 0 : if (bFilterEvent)
435 : : {
436 : 0 : if (pKeyEvent->type == KeyRelease)
437 : 0 : bFilterEvent = !maLastKeyPress.match (*pKeyEvent);
438 : 0 : maLastKeyPress.erase();
439 : : }
440 : : else /* (!bFilterEvent) */
441 : : {
442 : 0 : if (pKeyEvent->type == XLIB_KeyPress)
443 : 0 : maLastKeyPress = *pKeyEvent;
444 : : else
445 : 0 : maLastKeyPress.erase();
446 : : }
447 : :
448 : 0 : return bFilterEvent;
449 : : }
450 : :
451 : : void
452 : 0 : SalI18N_InputMethod::HandleDestroyIM()
453 : : {
454 : 0 : mbUseable = False;
455 : 0 : maMethod = NULL;
456 : 0 : }
457 : :
458 : : // ------------------------------------------------------------------------
459 : : //
460 : : // add a connection watch into the SalXLib yieldTable to allow iiimp
461 : : // connection processing: soffice waits in select() not in XNextEvent(), so
462 : : // there may be requests pending on the iiimp internal connection that will
463 : : // not be processed until XNextEvent is called the next time. If we do not
464 : : // have the focus because the atok12 lookup choice aux window has it we stay
465 : : // deaf and dump otherwise.
466 : : //
467 : : // ------------------------------------------------------------------------
468 : :
469 : : int
470 : 0 : InputMethod_HasPendingEvent(int nFileDescriptor, void *pData)
471 : : {
472 : 0 : if (pData == NULL)
473 : 0 : return 0;
474 : :
475 : : struct pollfd aFileDescriptor;
476 : : #ifdef SOLARIS
477 : : nfds_t nNumDescriptor = 1;
478 : : #else
479 : 0 : unsigned int nNumDescriptor = 1;
480 : : #endif
481 : 0 : aFileDescriptor.fd = nFileDescriptor;
482 : 0 : aFileDescriptor.events = POLLRDNORM;
483 : 0 : aFileDescriptor.revents = 0;
484 : :
485 : 0 : int nPoll = poll (&aFileDescriptor, nNumDescriptor, 0 /* timeout */ );
486 : :
487 : 0 : if (nPoll > 0)
488 : : {
489 : : /* at least some conditions in revent are set */
490 : 0 : if ( (aFileDescriptor.revents & POLLHUP)
491 : : || (aFileDescriptor.revents & POLLERR)
492 : : || (aFileDescriptor.revents & POLLNVAL))
493 : 0 : return 0; /* oops error condition set */
494 : :
495 : 0 : if (aFileDescriptor.revents & POLLRDNORM)
496 : 0 : return 1; /* success */
497 : : }
498 : :
499 : : /* nPoll == 0 means timeout, nPoll < 0 means error */
500 : 0 : return 0;
501 : : }
502 : :
503 : : int
504 : 0 : InputMethod_IsEventQueued(int nFileDescriptor, void *pData)
505 : : {
506 : 0 : return InputMethod_HasPendingEvent (nFileDescriptor, pData);
507 : : }
508 : :
509 : : int
510 : 0 : InputMethod_HandleNextEvent(int nFileDescriptor, void *pData)
511 : : {
512 : 0 : if (pData != NULL)
513 : 0 : XProcessInternalConnection((Display*)pData, nFileDescriptor);
514 : :
515 : 0 : return 0;
516 : : }
517 : :
518 : : extern "C" void
519 : 0 : InputMethod_ConnectionWatchProc (Display *pDisplay, XPointer pClientData,
520 : : int nFileDescriptor, Bool bOpening, XPointer*)
521 : : {
522 : 0 : SalXLib *pConnectionHandler = (SalXLib*)pClientData;
523 : :
524 : 0 : if (pConnectionHandler == NULL)
525 : 0 : return;
526 : :
527 : 0 : if (bOpening)
528 : : {
529 : : pConnectionHandler->Insert (nFileDescriptor, pDisplay,
530 : : InputMethod_HasPendingEvent,
531 : : InputMethod_IsEventQueued,
532 : 0 : InputMethod_HandleNextEvent);
533 : : }
534 : : else
535 : : {
536 : 0 : pConnectionHandler->Remove (nFileDescriptor);
537 : : }
538 : : }
539 : :
540 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|