1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
32 #include <unx/i18n_im.hxx>
34 #include <osl/thread.h>
35 #include <osl/process.h>
36 #include <sal/log.hxx>
38 #include <unx/i18n_cb.hxx>
42 // kinput2 IME needs special key handling since key release events are filtered in
43 // preeditmode and XmbResetIC does not work
47 class XKeyEventOp
: public XKeyEvent
55 XKeyEventOp
& operator= (const XKeyEvent
&rEvent
);
57 bool match (const XKeyEvent
&rEvent
) const;
65 type
= 0; /* serial = 0; */
66 send_event
= 0; display
= nullptr;
68 subwindow
= 0; /* time = 0; */
70 /* x_root = 0; y_root = 0; */
71 state
= 0; keycode
= 0;
75 XKeyEventOp::XKeyEventOp()
81 XKeyEventOp::operator= (const XKeyEvent
&rEvent
)
83 type
= rEvent
.type
; /* serial = rEvent.serial; */
84 send_event
= rEvent
.send_event
; display
= rEvent
.display
;
85 window
= rEvent
.window
; root
= rEvent
.root
;
86 subwindow
= rEvent
.subwindow
;/* time = rEvent.time; */
87 /* x = rEvent.x, y = rEvent.y; */
88 /* x_root = rEvent.x_root, y_root = rEvent.y_root; */
89 state
= rEvent
.state
; keycode
= rEvent
.keycode
;
90 same_screen
= rEvent
.same_screen
;
102 XKeyEventOp::match (const XKeyEvent
&rEvent
) const
104 return ( (type
== KeyPress
&& rEvent
.type
== KeyRelease
)
105 || (type
== KeyRelease
&& rEvent
.type
== KeyPress
))
106 /* && serial == rEvent.serial */
107 && send_event
== rEvent
.send_event
108 && display
== rEvent
.display
109 && window
== rEvent
.window
110 && root
== rEvent
.root
111 && subwindow
== rEvent
.subwindow
112 /* && time == rEvent.time
115 && x_root == rEvent.x_root
116 && y_root == rEvent.y_root */
117 && state
== rEvent
.state
118 && keycode
== rEvent
.keycode
119 && same_screen
== rEvent
.same_screen
;
124 // Locale handling of the operating system layer
127 SetSystemLocale( const char* p_inlocale
)
129 char *p_outlocale
= setlocale(LC_ALL
, p_inlocale
);
131 SAL_WARN_IF(p_outlocale
== nullptr, "vcl.app",
132 "I18N: Operating system doesn't support locale \""
133 << p_inlocale
<< "\".");
140 SetSystemEnvironment( const OUString
& rLocale
)
142 OUString
LC_ALL_Var("LC_ALL");
143 osl_setEnvironment(LC_ALL_Var
.pData
, rLocale
.pData
);
145 OUString
LANG_Var("LANG");
146 osl_setEnvironment(LANG_Var
.pData
, rLocale
.pData
);
151 IsPosixLocale( const char* p_locale
)
153 if ( p_locale
== nullptr )
155 if ( (p_locale
[ 0 ] == 'C') && (p_locale
[ 1 ] == '\0') )
157 if ( strncmp(p_locale
, "POSIX", sizeof("POSIX")) == 0 )
163 // Locale handling of the X Window System layer
166 IsXWindowCompatibleLocale( const char* p_locale
)
168 if ( p_locale
== nullptr )
171 if ( !XSupportsLocale() )
174 "I18N: X Window System doesn't support locale \""
175 << p_locale
<< "\".");
181 // Set the operating system locale prior to trying to open an
183 // Handle the cases where the current locale is either not supported by the
184 // operating system (LANG=gaga) or by the XWindow system (LANG=aa_ER@saaho)
185 // by providing a fallback.
186 // Upgrade "C" or "POSIX" to "en_US" locale to allow umlauts and accents
187 // see i8988, i9188, i8930, i16318
188 // on Solaris the environment needs to be set equivalent to the locale (#i37047#)
191 SalI18N_InputMethod::SetLocale()
193 // check whether we want an Input Method engine, if we don't we
194 // do not need to set the locale
198 char *locale
= SetSystemLocale( "" );
199 if ( (!IsXWindowCompatibleLocale(locale
)) || IsPosixLocale(locale
) )
201 osl_setThreadTextEncoding (RTL_TEXTENCODING_ISO_8859_1
);
202 locale
= SetSystemLocale( "en_US" );
204 SetSystemEnvironment( "en_US" );
206 if (! IsXWindowCompatibleLocale(locale
))
208 locale
= SetSystemLocale( "C" );
210 SetSystemEnvironment( "C" );
212 if (! IsXWindowCompatibleLocale(locale
))
217 // must not fail if mbUseable since XSupportsLocale() asserts success
218 if ( mbUseable
&& XSetLocaleModifiers("") == nullptr )
221 "I18N: Can't set X modifiers for locale \""
228 SalI18N_InputMethod::PosixLocale()
231 return IsPosixLocale (XLocaleOfIM (maMethod
));
235 // Constructor / Destructor / Initialisation
237 SalI18N_InputMethod::SalI18N_InputMethod( )
238 : mbUseable( bUseInputMethodDefault
)
239 , maMethod( nullptr )
240 , mpStyles( nullptr )
242 maDestroyCallback
.callback
= nullptr;
243 maDestroyCallback
.client_data
= nullptr;
244 const char *pUseInputMethod
= getenv( "SAL_USEINPUTMETHOD" );
245 if ( pUseInputMethod
!= nullptr )
246 mbUseable
= pUseInputMethod
[0] != '\0' ;
249 SalI18N_InputMethod::~SalI18N_InputMethod()
251 if ( mpStyles
!= nullptr )
253 if ( maMethod
!= nullptr )
254 XCloseIM ( maMethod
);
258 // debug routine: lets have a look at the provided method styles
260 #if OSL_DEBUG_LEVEL > 1
263 GetMethodName( XIMStyle nStyle
, char *pBuf
, int nBufSize
)
266 const XIMStyle nStyle
;
272 static const StyleName pDescription
[] = {
273 { XIMPreeditArea
, "PreeditArea ", sizeof("PreeditArea ") },
274 { XIMPreeditCallbacks
, "PreeditCallbacks ",sizeof("PreeditCallbacks ")},
275 { XIMPreeditPosition
, "PreeditPosition ", sizeof("PreeditPosition ") },
276 { XIMPreeditNothing
, "PreeditNothing ", sizeof("PreeditNothing ") },
277 { XIMPreeditNone
, "PreeditNone ", sizeof("PreeditNone ") },
278 { XIMStatusArea
, "StatusArea ", sizeof("StatusArea ") },
279 { XIMStatusCallbacks
, "StatusCallbacks ", sizeof("StatusCallbacks ") },
280 { XIMStatusNothing
, "StatusNothing ", sizeof("StatusNothing ") },
281 { XIMStatusNone
, "StatusNone ", sizeof("StatusNone ") },
288 char *pBufPtr
= pBuf
;
289 for ( pDescPtr
= const_cast<StyleName
*>(pDescription
); pDescPtr
->nStyle
!= 0; pDescPtr
++ )
291 int nSize
= pDescPtr
->nNameLen
- 1;
292 if ( (nStyle
& pDescPtr
->nStyle
) && (nBufSize
> nSize
) )
294 strncpy( pBufPtr
, pDescPtr
->pName
, nSize
+ 1);
304 PrintInputStyle( XIMStyles
*pStyle
)
307 int nBuf
= sizeof( pBuf
);
309 if ( pStyle
== NULL
)
310 SAL_INFO("vcl.app", "no input method styles.");
312 for ( int nStyle
= 0; nStyle
< pStyle
->count_styles
; nStyle
++ )
314 SAL_INFO("vcl.app", "style #"
317 << GetMethodName(pStyle
->supported_styles
[nStyle
], pBuf
, nBuf
));
323 // this is the real constructing routine, since locale setting has to be done
324 // prior to xopendisplay, the xopenim call has to be delayed
327 SalI18N_InputMethod::CreateMethod ( Display
*pDisplay
)
331 maMethod
= XOpenIM(pDisplay
, nullptr, nullptr, nullptr);
333 if ((maMethod
== nullptr) && (getenv("XMODIFIERS") != nullptr))
335 OUString
envVar("XMODIFIERS");
336 osl_clearEnvironment(envVar
.pData
);
337 XSetLocaleModifiers("");
338 maMethod
= XOpenIM(pDisplay
, nullptr, nullptr, nullptr);
341 if ( maMethod
!= nullptr )
343 if ( XGetIMValues(maMethod
, XNQueryInputStyle
, &mpStyles
, nullptr)
346 #if OSL_DEBUG_LEVEL > 1
347 SAL_INFO("vcl.app", "Creating Mono-Lingual InputMethod.");
348 PrintInputStyle( mpStyles
);
357 #if OSL_DEBUG_LEVEL > 1
358 SAL_WARN_IF(!mbUseable
, "vcl.app", "input method creation failed.");
361 maDestroyCallback
.callback
= IM_IMDestroyCallback
;
362 maDestroyCallback
.client_data
= reinterpret_cast<XPointer
>(this);
363 if (mbUseable
&& maMethod
!= nullptr)
364 XSetIMValues(maMethod
, XNDestroyCallback
, &maDestroyCallback
, nullptr);
367 // give IM the opportunity to look at the event, and possibly hide it
370 SalI18N_InputMethod::FilterEvent( XEvent
*pEvent
, ::Window window
)
375 bool bFilterEvent
= XFilterEvent (pEvent
, window
);
377 if (pEvent
->type
!= KeyPress
&& pEvent
->type
!= KeyRelease
)
381 * fix broken key release handling of some IMs
383 XKeyEvent
* pKeyEvent
= &(pEvent
->xkey
);
384 static XKeyEventOp s_aLastKeyPress
;
388 if (pKeyEvent
->type
== KeyRelease
)
389 bFilterEvent
= !s_aLastKeyPress
.match (*pKeyEvent
);
390 s_aLastKeyPress
.erase();
392 else /* (!bFilterEvent) */
394 if (pKeyEvent
->type
== KeyPress
)
395 s_aLastKeyPress
= *pKeyEvent
;
397 s_aLastKeyPress
.erase();
404 SalI18N_InputMethod::HandleDestroyIM()
410 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */