1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: i18n_im.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_vcl.hxx"
43 // for SetSystemEnvironment()
44 #include <sal/alloca.h>
47 #include <tools/prex.h>
48 #include <X11/Xlocale.h>
51 #include <tools/postx.h>
54 #include <saldisp.hxx>
55 #include "i18n_im.hxx"
56 #include <i18n_status.hxx>
58 #include <osl/thread.h>
61 #include "i18n_cb.hxx"
62 #if defined(SOLARIS) || defined(LINUX) || defined(IRIX)
63 extern "C" char * XSetIMValues(XIM im
, ...);
66 // ------------------------------------------------------------------------------------
68 // kinput2 IME needs special key handling since key release events are filtered in
69 // preeditmode and XmbResetIC does not work
71 // ------------------------------------------------------------------------------------
76 const static char* p_xmodifiers
= getenv ("XMODIFIERS");
77 const static Bool b_kinput2
= (p_xmodifiers
!= NULL
)
78 && (strcmp(p_xmodifiers
, "@im=kinput2") == 0);
83 class XKeyEventOp
: XKeyEvent
92 XKeyEventOp
& operator= (const XKeyEvent
&rEvent
);
94 Bool
match (const XKeyEvent
&rEvent
) const;
100 type
= 0; /* serial = 0; */
101 send_event
= 0; display
= 0;
102 window
= 0; root
= 0;
103 subwindow
= 0; /* time = 0; */
105 /* x_root = 0; y_root = 0; */
106 state
= 0; keycode
= 0;
110 XKeyEventOp::XKeyEventOp()
115 XKeyEventOp::~XKeyEventOp()
120 XKeyEventOp::operator= (const XKeyEvent
&rEvent
)
122 type
= rEvent
.type
; /* serial = rEvent.serial; */
123 send_event
= rEvent
.send_event
; display
= rEvent
.display
;
124 window
= rEvent
.window
; root
= rEvent
.root
;
125 subwindow
= rEvent
.subwindow
;/* time = rEvent.time; */
126 /* x = rEvent.x, y = rEvent.y; */
127 /* x_root = rEvent.x_root, y_root = rEvent.y_root; */
128 state
= rEvent
.state
; keycode
= rEvent
.keycode
;
129 same_screen
= rEvent
.same_screen
;
135 XKeyEventOp::erase ()
141 XKeyEventOp::match (const XKeyEvent
&rEvent
) const
143 return ( (type
== XLIB_KeyPress
&& rEvent
.type
== KeyRelease
)
144 || (type
== KeyRelease
&& rEvent
.type
== XLIB_KeyPress
))
145 /* && serial == rEvent.serial */
146 && send_event
== rEvent
.send_event
147 && display
== rEvent
.display
148 && window
== rEvent
.window
149 && root
== rEvent
.root
150 && subwindow
== rEvent
.subwindow
151 /* && time == rEvent.time
154 && x_root == rEvent.x_root
155 && y_root == rEvent.y_root */
156 && state
== rEvent
.state
157 && keycode
== rEvent
.keycode
158 && same_screen
== rEvent
.same_screen
;
161 // -------------------------------------------------------------------------
165 // -------------------------------------------------------------------------
167 // Locale handling of the operating system layer
170 SetSystemLocale( const char* p_inlocale
)
174 if ( (p_outlocale
= setlocale(LC_ALL
, p_inlocale
)) == NULL
)
176 fprintf( stderr
, "I18N: Operating system doesn't support locale \"%s\"\n",
185 SetSystemEnvironment( const char* p_locale
)
187 const char *lc_all
= "LC_ALL=%s";
188 const char *lang
= "LANG=%s";
192 if (p_locale
!= NULL
)
194 p_buffer
= (char*)alloca(10 + strlen(p_locale
));
195 sprintf(p_buffer
, lc_all
, p_locale
);
196 putenv(strdup(p_buffer
));
197 sprintf(p_buffer
, lang
, p_locale
);
198 putenv(strdup(p_buffer
));
204 IsPosixLocale( const char* p_locale
)
206 if ( p_locale
== NULL
)
208 if ( (p_locale
[ 0 ] == 'C') && (p_locale
[ 1 ] == '\0') )
210 if ( strncmp(p_locale
, "POSIX", sizeof("POSIX")) == 0 )
216 // Locale handling of the X Window System layer
219 IsXWindowCompatibleLocale( const char* p_locale
)
221 if ( p_locale
== NULL
)
224 if ( !XSupportsLocale() )
226 fprintf (stderr
, "I18N: X Window System doesn't support locale \"%s\"\n",
233 // Set the operating system locale prior to trying to open an
235 // Handle the cases where the current locale is either not supported by the
236 // operating system (LANG=gaga) or by the XWindow system (LANG=aa_ER@saaho)
237 // by providing a fallback.
238 // Upgrade "C" or "POSIX" to "en_US" locale to allow umlauts and accents
239 // see i8988, i9188, i8930, i16318
240 // on Solaris the environment needs to be set equivalent to the locale (#i37047#)
243 SalI18N_InputMethod::SetLocale( const char* pLocale
)
245 // check whether we want an Input Method engine, if we don't we
246 // do not need to set the locale
249 char *locale
= SetSystemLocale( pLocale
);
250 if ( (!IsXWindowCompatibleLocale(locale
)) || IsPosixLocale(locale
) )
252 osl_setThreadTextEncoding (RTL_TEXTENCODING_ISO_8859_1
);
253 locale
= SetSystemLocale( "en_US" );
255 SetSystemEnvironment( "en_US" );
257 if (! IsXWindowCompatibleLocale(locale
))
259 locale
= SetSystemLocale( "C" );
261 SetSystemEnvironment( "C" );
263 if (! IsXWindowCompatibleLocale(locale
))
268 // must not fail if mbUseable since XSupportsLocale() asserts success
269 if ( mbUseable
&& XSetLocaleModifiers("") == NULL
)
271 fprintf (stderr
, "I18N: Can't set X modifiers for locale \"%s\"\n",
281 SalI18N_InputMethod::PosixLocale()
286 return IsPosixLocale (XLocaleOfIM (maMethod
));
290 // ------------------------------------------------------------------------
292 // Constructor / Destructor / Initialisation
294 // ------------------------------------------------------------------------
296 SalI18N_InputMethod::SalI18N_InputMethod( ) : mbUseable( bUseInputMethodDefault
),
297 mbMultiLingual( False
),
298 maMethod( (XIM
)NULL
),
299 mpStyles( (XIMStyles
*)NULL
)
301 const char *pUseInputMethod
= getenv( "SAL_USEINPUTMETHOD" );
302 if ( pUseInputMethod
!= NULL
)
303 mbUseable
= pUseInputMethod
[0] != '\0' ;
306 SalI18N_InputMethod::~SalI18N_InputMethod()
308 ::vcl::I18NStatus::free();
309 if ( mpStyles
!= NULL
)
311 if ( maMethod
!= NULL
)
312 XCloseIM ( maMethod
);
317 // debug routine: lets have a look at the provided method styles
320 #if OSL_DEBUG_LEVEL > 1
323 GetMethodName( XIMStyle nStyle
, char *pBuf
, int nBufSize
)
326 const XIMStyle nStyle
;
332 static const StyleName pDescription
[] = {
333 { XIMPreeditArea
, "PreeditArea ", sizeof("PreeditArea ") },
334 { XIMPreeditCallbacks
, "PreeditCallbacks ",sizeof("PreeditCallbacks ")},
335 { XIMPreeditPosition
, "PreeditPosition ", sizeof("PreeditPosition ") },
336 { XIMPreeditNothing
, "PreeditNothing ", sizeof("PreeditNothing ") },
337 { XIMPreeditNone
, "PreeditNone ", sizeof("PreeditNone ") },
338 { XIMStatusArea
, "StatusArea ", sizeof("StatusArea ") },
339 { XIMStatusCallbacks
, "StatusCallbacks ", sizeof("StatusCallbacks ") },
340 { XIMStatusNothing
, "StatusNothing ", sizeof("StatusNothing ") },
341 { XIMStatusNone
, "StatusNone ", sizeof("StatusNone ") },
348 char *pBufPtr
= pBuf
;
349 for ( pDescPtr
= const_cast<StyleName
*>(pDescription
); pDescPtr
->nStyle
!= 0; pDescPtr
++ )
351 int nSize
= pDescPtr
->nNameLen
- 1;
352 if ( (nStyle
& pDescPtr
->nStyle
) && (nBufSize
> nSize
) )
354 strncpy( pBufPtr
, pDescPtr
->pName
, nSize
+ 1);
364 PrintInputStyle( XIMStyles
*pStyle
)
367 int nBuf
= sizeof( pBuf
);
369 if ( pStyle
== NULL
)
370 fprintf( stderr
, "no input method styles\n");
372 for ( int nStyle
= 0; nStyle
< pStyle
->count_styles
; nStyle
++ )
374 fprintf( stderr
, "style #%i = %s\n", nStyle
,
375 GetMethodName(pStyle
->supported_styles
[nStyle
], pBuf
, nBuf
) );
382 // this is the real constructing routine, since locale setting has to be done
383 // prior to xopendisplay, the xopenim call has to be delayed
387 SalI18N_InputMethod::CreateMethod ( Display
*pDisplay
)
391 const bool bTryMultiLingual
=
397 if ( bTryMultiLingual
&& getenv("USE_XOPENIM") == NULL
)
399 mbMultiLingual
= True
; // set ml-input flag to create input-method
400 maMethod
= XvaOpenIM(pDisplay
, NULL
, NULL
, NULL
,
401 XNMultiLingualInput
, mbMultiLingual
, /* dummy */
403 // get ml-input flag from input-method
404 if ( maMethod
== (XIM
)NULL
)
405 mbMultiLingual
= False
;
407 if ( XGetIMValues(maMethod
,
408 XNMultiLingualInput
, &mbMultiLingual
, NULL
) != NULL
)
409 mbMultiLingual
= False
;
412 XIMUnicodeCharacterSubsets
* subsets
;
413 if( XGetIMValues( maMethod
,
414 XNQueryUnicodeCharacterSubset
, &subsets
, NULL
) == NULL
)
416 #if OSL_DEBUG_LEVEL > 1
417 fprintf( stderr
, "IM reports %d subsets: ", subsets
->count_subsets
);
419 I18NStatus
& rStatus( I18NStatus::get() );
420 rStatus
.clearChoices();
421 for( int i
= 0; i
< subsets
->count_subsets
; i
++ )
423 #if OSL_DEBUG_LEVEL > 1
424 fprintf( stderr
,"\"%s\" ", subsets
->supported_subsets
[i
].name
);
426 rStatus
.addChoice( String( subsets
->supported_subsets
[i
].name
, RTL_TEXTENCODING_UTF8
), &subsets
->supported_subsets
[i
] );
428 #if OSL_DEBUG_LEVEL > 1
429 fprintf( stderr
, "\n" );
432 #if OSL_DEBUG_LEVEL > 1
434 fprintf( stderr
, "query subsets failed\n" );
440 maMethod
= XOpenIM(pDisplay
, NULL
, NULL
, NULL
);
441 mbMultiLingual
= False
;
444 if ((maMethod
== (XIM
)NULL
) && (getenv("XMODIFIERS") != NULL
))
446 putenv (strdup("XMODIFIERS"));
447 XSetLocaleModifiers("");
448 maMethod
= XOpenIM(pDisplay
, NULL
, NULL
, NULL
);
449 mbMultiLingual
= False
;
452 if ( maMethod
!= (XIM
)NULL
)
454 if ( XGetIMValues(maMethod
, XNQueryInputStyle
, &mpStyles
, NULL
)
457 #if OSL_DEBUG_LEVEL > 1
458 fprintf(stderr
, "Creating %s-Lingual InputMethod\n",
459 mbMultiLingual
? "Multi" : "Mono" );
460 PrintInputStyle( mpStyles
);
469 #if OSL_DEBUG_LEVEL > 1
471 fprintf(stderr
, "input method creation failed\n");
474 maDestroyCallback
.callback
= (XIMProc
)IM_IMDestroyCallback
;
475 maDestroyCallback
.client_data
= (XPointer
)this;
476 if (mbUseable
&& maMethod
!= NULL
)
477 XSetIMValues(maMethod
, XNDestroyCallback
, &maDestroyCallback
, NULL
);
483 // give IM the opportunity to look at the event, and possibly hide it
487 SalI18N_InputMethod::FilterEvent( XEvent
*pEvent
, XLIB_Window window
)
492 Bool bFilterEvent
= XFilterEvent (pEvent
, window
);
494 if (pEvent
->type
!= XLIB_KeyPress
&& pEvent
->type
!= KeyRelease
)
498 * fix broken key release handling of some IMs
500 XKeyEvent
* pKeyEvent
= &(pEvent
->xkey
);
501 static XKeyEventOp maLastKeyPress
;
505 if (pKeyEvent
->type
== KeyRelease
)
506 bFilterEvent
= !maLastKeyPress
.match (*pKeyEvent
);
507 maLastKeyPress
.erase();
509 else /* (!bFilterEvent) */
511 if (pKeyEvent
->type
== XLIB_KeyPress
)
512 maLastKeyPress
= *pKeyEvent
;
514 maLastKeyPress
.erase();
521 SalI18N_InputMethod::HandleDestroyIM()
524 mbMultiLingual
= False
;
528 // ------------------------------------------------------------------------
530 // add a connection watch into the SalXLib yieldTable to allow iiimp
531 // connection processing: soffice waits in select() not in XNextEvent(), so
532 // there may be requests pending on the iiimp internal connection that will
533 // not be processed until XNextEvent is called the next time. If we do not
534 // have the focus because the atok12 lookup choice aux window has it we stay
535 // deaf and dump otherwise.
537 // ------------------------------------------------------------------------
540 InputMethod_HasPendingEvent(int nFileDescriptor
, void *pData
)
545 struct pollfd aFileDescriptor
;
547 nfds_t nNumDescriptor
= 1;
549 unsigned int nNumDescriptor
= 1;
551 aFileDescriptor
.fd
= nFileDescriptor
;
552 aFileDescriptor
.events
= POLLRDNORM
;
553 aFileDescriptor
.revents
= 0;
555 int nPoll
= poll (&aFileDescriptor
, nNumDescriptor
, 0 /* timeout */ );
559 /* at least some conditions in revent are set */
560 if ( (aFileDescriptor
.revents
& POLLHUP
)
561 || (aFileDescriptor
.revents
& POLLERR
)
562 || (aFileDescriptor
.revents
& POLLNVAL
))
563 return 0; /* oops error condition set */
565 if (aFileDescriptor
.revents
& POLLRDNORM
)
566 return 1; /* success */
569 /* nPoll == 0 means timeout, nPoll < 0 means error */
574 InputMethod_IsEventQueued(int nFileDescriptor
, void *pData
)
576 return InputMethod_HasPendingEvent (nFileDescriptor
, pData
);
580 InputMethod_HandleNextEvent(int nFileDescriptor
, void *pData
)
583 XProcessInternalConnection((Display
*)pData
, nFileDescriptor
);
589 InputMethod_ConnectionWatchProc (Display
*pDisplay
, XPointer pClientData
,
590 int nFileDescriptor
, Bool bOpening
, XPointer
*)
592 SalXLib
*pConnectionHandler
= (SalXLib
*)pClientData
;
594 if (pConnectionHandler
== NULL
)
599 pConnectionHandler
->Insert (nFileDescriptor
, pDisplay
,
600 InputMethod_HasPendingEvent
,
601 InputMethod_IsEventQueued
,
602 InputMethod_HandleNextEvent
);
606 pConnectionHandler
->Remove (nFileDescriptor
);
611 SalI18N_InputMethod::AddConnectionWatch(Display
*pDisplay
, void *pConnectionHandler
)
614 if (pDisplay
== NULL
|| pConnectionHandler
== NULL
)
617 // if we are not ml all the extended text input comes on the stock X queue,
618 // so there is no need to monitor additional file descriptors.
620 if (!mbMultiLingual
|| !mbUseable
)
624 // pConnectionHandler must be really a pointer to a SalXLib
625 Status nStatus
= XAddConnectionWatch (pDisplay
, InputMethod_ConnectionWatchProc
,
626 (XPointer
)pConnectionHandler
);
627 return (Bool
)nStatus
;