2 * Functions for further XIM control
4 * Copyright 2003 CodeWeavers, Aric Stewart
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #define WIN32_NO_STATUS
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(xim
);
42 #ifndef HAVE_XICCALLBACK_CALLBACK
43 #define XICCallback XIMCallback
44 #define XICProc XIMProc
47 static WCHAR
*ime_comp_buf
;
49 static XIMStyle input_style
= 0;
50 static XIMStyle input_style_req
= XIMPreeditCallbacks
| XIMStatusCallbacks
;
52 static const char *debugstr_xim_style( XIMStyle style
)
54 char buffer
[1024], *buf
= buffer
;
56 buf
+= sprintf( buf
, "preedit" );
57 if (style
& XIMPreeditArea
) buf
+= sprintf( buf
, " area" );
58 if (style
& XIMPreeditCallbacks
) buf
+= sprintf( buf
, " callbacks" );
59 if (style
& XIMPreeditPosition
) buf
+= sprintf( buf
, " position" );
60 if (style
& XIMPreeditNothing
) buf
+= sprintf( buf
, " nothing" );
61 if (style
& XIMPreeditNone
) buf
+= sprintf( buf
, " none" );
63 buf
+= sprintf( buf
, ", status" );
64 if (style
& XIMStatusArea
) buf
+= sprintf( buf
, " area" );
65 if (style
& XIMStatusCallbacks
) buf
+= sprintf( buf
, " callbacks" );
66 if (style
& XIMStatusNothing
) buf
+= sprintf( buf
, " nothing" );
67 if (style
& XIMStatusNone
) buf
+= sprintf( buf
, " none" );
69 return wine_dbg_sprintf( "%s", buffer
);
72 BOOL
xim_in_compose_mode(void)
74 return !!ime_comp_buf
;
77 static void post_ime_update( HWND hwnd
, UINT cursor_pos
, WCHAR
*comp_str
, WCHAR
*result_str
)
79 NtUserMessageCall( hwnd
, WINE_IME_POST_UPDATE
, cursor_pos
, (LPARAM
)comp_str
,
80 result_str
, NtUserImeDriverCall
, FALSE
);
83 static void xim_update_comp_string( UINT offset
, UINT old_len
, const WCHAR
*text
, UINT new_len
)
85 UINT len
= ime_comp_buf
? wcslen( ime_comp_buf
) : 0;
86 int diff
= new_len
- old_len
;
89 TRACE( "offset %u, old_len %u, text %s\n", offset
, old_len
, debugstr_wn(text
, new_len
) );
91 if (!(ptr
= realloc( ime_comp_buf
, (len
+ max(0, diff
) + 1) * sizeof(WCHAR
) )))
93 ERR( "Failed to reallocate composition string buffer\n" );
98 ptr
= ime_comp_buf
+ offset
;
99 memmove( ptr
+ new_len
, ptr
+ old_len
, (len
- offset
- old_len
) * sizeof(WCHAR
) );
100 if (text
) memcpy( ptr
, text
, new_len
* sizeof(WCHAR
) );
101 ime_comp_buf
[len
+ diff
] = 0;
104 void xim_set_result_string( HWND hwnd
, const char *str
, UINT count
)
109 TRACE( "hwnd %p, string %s\n", hwnd
, debugstr_an(str
, count
) );
111 if (!(output
= malloc( (count
+ 1) * sizeof(WCHAR
) ))) return;
112 len
= ntdll_umbstowcs( str
, count
, output
, count
);
115 post_ime_update( hwnd
, 0, NULL
, output
);
120 static BOOL
xic_preedit_state_notify( XIC xic
, XPointer user
, XPointer arg
)
122 XIMPreeditStateNotifyCallbackStruct
*params
= (void *)arg
;
123 const XIMPreeditState state
= params
->state
;
124 HWND hwnd
= (HWND
)user
;
126 TRACE( "xic %p, hwnd %p, state %lu\n", xic
, hwnd
, state
);
130 case XIMPreeditEnable
:
131 NtUserPostMessage( hwnd
, WM_IME_NOTIFY
, IMN_WINE_SET_OPEN_STATUS
, TRUE
);
133 case XIMPreeditDisable
:
134 NtUserPostMessage( hwnd
, WM_IME_NOTIFY
, IMN_WINE_SET_OPEN_STATUS
, FALSE
);
141 static int xic_preedit_start( XIC xic
, XPointer user
, XPointer arg
)
143 HWND hwnd
= (HWND
)user
;
145 TRACE( "xic %p, hwnd %p, arg %p\n", xic
, hwnd
, arg
);
147 if ((ime_comp_buf
= realloc( ime_comp_buf
, sizeof(WCHAR
) ))) *ime_comp_buf
= 0;
148 else ERR( "Failed to allocate preedit buffer\n" );
150 NtUserPostMessage( hwnd
, WM_IME_NOTIFY
, IMN_WINE_SET_OPEN_STATUS
, TRUE
);
151 post_ime_update( hwnd
, 0, ime_comp_buf
, NULL
);
156 static int xic_preedit_done( XIC xic
, XPointer user
, XPointer arg
)
158 HWND hwnd
= (HWND
)user
;
160 TRACE( "xic %p, hwnd %p, arg %p\n", xic
, hwnd
, arg
);
162 free( ime_comp_buf
);
165 post_ime_update( hwnd
, 0, NULL
, NULL
);
166 NtUserPostMessage( hwnd
, WM_IME_NOTIFY
, IMN_WINE_SET_OPEN_STATUS
, FALSE
);
171 static int xic_preedit_draw( XIC xic
, XPointer user
, XPointer arg
)
173 XIMPreeditDrawCallbackStruct
*params
= (void *)arg
;
174 HWND hwnd
= (HWND
)user
;
181 TRACE( "xic %p, hwnd %p, arg %p\n", xic
, hwnd
, arg
);
183 if (!params
) return 0;
185 if (!(text
= params
->text
)) str
= NULL
;
186 else if (!text
->encoding_is_wchar
) str
= text
->string
.multi_byte
;
187 else if ((len
= wcstombs( NULL
, text
->string
.wide_char
, text
->length
)) < 0) str
= NULL
;
188 else if ((str
= malloc( len
+ 1 )))
190 wcstombs( str
, text
->string
.wide_char
, len
);
194 if (!str
|| !(text_len
= strlen( str
)) || !(output
= malloc( text_len
* sizeof(WCHAR
) )))
195 xim_update_comp_string( params
->chg_first
, params
->chg_length
, NULL
, 0 );
198 text_len
= ntdll_umbstowcs( str
, text_len
, output
, text_len
);
199 xim_update_comp_string( params
->chg_first
, params
->chg_length
, output
, text_len
);
203 if (text
&& str
!= text
->string
.multi_byte
) free( str
);
205 post_ime_update( hwnd
, params
->caret
, ime_comp_buf
, NULL
);
210 static int xic_preedit_caret( XIC xic
, XPointer user
, XPointer arg
)
212 static int xim_caret_pos
;
213 XIMPreeditCaretCallbackStruct
*params
= (void *)arg
;
214 HWND hwnd
= (HWND
)user
;
217 TRACE( "xic %p, hwnd %p, arg %p\n", xic
, hwnd
, arg
);
219 if (!params
) return 0;
222 switch (params
->direction
)
228 case XIMBackwardChar
:
229 case XIMBackwardWord
:
235 case XIMAbsolutePosition
:
236 pos
= params
->position
;
239 params
->position
= pos
;
243 case XIMPreviousLine
:
246 FIXME( "Not implemented\n" );
249 params
->position
= xim_caret_pos
= pos
;
251 post_ime_update( hwnd
, pos
, ime_comp_buf
, NULL
);
256 static int xic_status_start( XIC xic
, XPointer user
, XPointer arg
)
258 HWND hwnd
= (HWND
)user
;
259 TRACE( "xic %p, hwnd %p, arg %p\n", xic
, hwnd
, arg
);
263 static int xic_status_done( XIC xic
, XPointer user
, XPointer arg
)
265 HWND hwnd
= (HWND
)user
;
266 TRACE( "xic %p, hwnd %p, arg %p\n", xic
, hwnd
, arg
);
270 static int xic_status_draw( XIC xic
, XPointer user
, XPointer arg
)
272 HWND hwnd
= (HWND
)user
;
273 TRACE( "xic %p, hwnd %p, arg %p\n", xic
, hwnd
, arg
);
277 /***********************************************************************
278 * NotifyIMEStatus (X11DRV.@)
280 void X11DRV_NotifyIMEStatus( HWND hwnd
, UINT status
)
282 XIMPreeditState state
= status
? XIMPreeditEnable
: XIMPreeditDisable
;
286 TRACE( "hwnd %p, status %#x\n", hwnd
, status
);
288 if (!(xic
= X11DRV_get_ic( hwnd
))) return;
290 if ((attr
= XVaCreateNestedList( 0, XNPreeditState
, state
, NULL
)))
292 XSetICValues( xic
, XNPreeditAttributes
, attr
, NULL
);
296 if (!status
) XFree( XmbResetIC( xic
) );
299 /***********************************************************************
302 BOOL
xim_init( const WCHAR
*input_style
)
304 static const WCHAR offthespotW
[] = {'o','f','f','t','h','e','s','p','o','t',0};
305 static const WCHAR overthespotW
[] = {'o','v','e','r','t','h','e','s','p','o','t',0};
306 static const WCHAR rootW
[] = {'r','o','o','t',0};
308 if (!XSupportsLocale())
310 WARN("X does not support locale.\n");
313 if (XSetLocaleModifiers("") == NULL
)
315 WARN("Could not set locale modifiers.\n");
319 if (!wcsicmp( input_style
, offthespotW
))
320 input_style_req
= XIMPreeditArea
| XIMStatusArea
;
321 else if (!wcsicmp( input_style
, overthespotW
))
322 input_style_req
= XIMPreeditPosition
| XIMStatusNothing
;
323 else if (!wcsicmp( input_style
, rootW
))
324 input_style_req
= XIMPreeditNothing
| XIMStatusNothing
;
326 TRACE( "requesting %s style %#lx %s\n", debugstr_w(input_style
), input_style_req
,
327 debugstr_xim_style( input_style_req
) );
332 static void xim_open( Display
*display
, XPointer user
, XPointer arg
);
333 static void xim_destroy( XIM xim
, XPointer user
, XPointer arg
);
335 static XIM
xim_create( struct x11drv_thread_data
*data
)
337 XIMCallback destroy
= {.callback
= xim_destroy
, .client_data
= (XPointer
)data
};
338 XIMStyle input_style_fallback
= XIMPreeditNone
| XIMStatusNone
;
339 XIMStyles
*styles
= NULL
;
343 if (!(xim
= XOpenIM( data
->display
, NULL
, NULL
, NULL
)))
345 WARN("Could not open input method.\n");
349 if (XSetIMValues( xim
, XNDestroyCallback
, &destroy
, NULL
))
350 WARN( "Could not set destroy callback.\n" );
352 TRACE( "xim %p, XDisplayOfIM %p, XLocaleOfIM %s\n", xim
, XDisplayOfIM( xim
),
353 debugstr_a(XLocaleOfIM( xim
)) );
355 XGetIMValues( xim
, XNQueryInputStyle
, &styles
, NULL
);
358 WARN( "Could not find supported input style.\n" );
363 TRACE( "input styles count %u\n", styles
->count_styles
);
364 for (i
= 0, input_style
= 0; i
< styles
->count_styles
; ++i
)
366 XIMStyle style
= styles
->supported_styles
[i
];
367 TRACE( " %u: %#lx %s\n", i
, style
, debugstr_xim_style( style
) );
369 if (style
== input_style_req
) input_style
= style
;
370 if (!input_style
&& (style
& input_style_req
)) input_style
= style
;
371 if (input_style_fallback
> style
) input_style_fallback
= style
;
375 if (!input_style
) input_style
= input_style_fallback
;
376 TRACE( "selected style %#lx %s\n", input_style
, debugstr_xim_style( input_style
) );
381 static void xim_open( Display
*display
, XPointer user
, XPointer arg
)
383 struct x11drv_thread_data
*data
= (void *)user
;
384 TRACE( "display %p, data %p, arg %p\n", display
, user
, arg
);
385 if (!(data
->xim
= xim_create( data
))) return;
386 XUnregisterIMInstantiateCallback( display
, NULL
, NULL
, NULL
, xim_open
, user
);
389 static void xim_destroy( XIM xim
, XPointer user
, XPointer arg
)
391 struct x11drv_thread_data
*data
= x11drv_thread_data();
392 TRACE( "xim %p, user %p, arg %p\n", xim
, user
, arg
);
393 if (data
->xim
!= xim
) return;
395 XRegisterIMInstantiateCallback( data
->display
, NULL
, NULL
, NULL
, xim_open
, user
);
398 void xim_thread_attach( struct x11drv_thread_data
*data
)
400 Display
*display
= data
->display
;
404 data
->font_set
= XCreateFontSet( display
, "fixed", &list
, &count
, NULL
);
405 TRACE( "created XFontSet %p, list %p, count %d\n", data
->font_set
, list
, count
);
406 for (i
= 0; list
&& i
< count
; ++i
) TRACE( " %d: %s\n", i
, list
[i
] );
407 if (list
) XFreeStringList( list
);
409 if ((data
->xim
= xim_create( data
))) return;
410 XRegisterIMInstantiateCallback( display
, NULL
, NULL
, NULL
, xim_open
, (XPointer
)data
);
413 static BOOL
xic_destroy( XIC xic
, XPointer user
, XPointer arg
)
415 struct x11drv_win_data
*data
;
416 HWND hwnd
= (HWND
)user
;
418 TRACE( "xic %p, hwnd %p, arg %p\n", xic
, hwnd
, arg
);
420 if ((data
= get_win_data( hwnd
)))
422 if (data
->xic
== xic
) data
->xic
= NULL
;
423 release_win_data( data
);
429 static XIC
xic_create( XIM xim
, HWND hwnd
, Window win
)
431 XICCallback destroy
= {.callback
= xic_destroy
, .client_data
= (XPointer
)hwnd
};
432 XICCallback preedit_caret
= {.callback
= xic_preedit_caret
, .client_data
= (XPointer
)hwnd
};
433 XICCallback preedit_done
= {.callback
= xic_preedit_done
, .client_data
= (XPointer
)hwnd
};
434 XICCallback preedit_draw
= {.callback
= xic_preedit_draw
, .client_data
= (XPointer
)hwnd
};
435 XICCallback preedit_start
= {.callback
= xic_preedit_start
, .client_data
= (XPointer
)hwnd
};
436 XICCallback preedit_state_notify
= {.callback
= xic_preedit_state_notify
, .client_data
= (XPointer
)hwnd
};
437 XICCallback status_done
= {.callback
= xic_status_done
, .client_data
= (XPointer
)hwnd
};
438 XICCallback status_draw
= {.callback
= xic_status_draw
, .client_data
= (XPointer
)hwnd
};
439 XICCallback status_start
= {.callback
= xic_status_start
, .client_data
= (XPointer
)hwnd
};
441 XVaNestedList preedit
, status
;
443 XFontSet fontSet
= x11drv_thread_data()->font_set
;
445 TRACE( "xim %p, hwnd %p/%lx\n", xim
, hwnd
, win
);
447 preedit
= XVaCreateNestedList( 0, XNFontSet
, fontSet
,
448 XNPreeditCaretCallback
, &preedit_caret
,
449 XNPreeditDoneCallback
, &preedit_done
,
450 XNPreeditDrawCallback
, &preedit_draw
,
451 XNPreeditStartCallback
, &preedit_start
,
452 XNPreeditStateNotifyCallback
, &preedit_state_notify
,
453 XNSpotLocation
, &spot
, NULL
);
454 status
= XVaCreateNestedList( 0, XNFontSet
, fontSet
,
455 XNStatusStartCallback
, &status_start
,
456 XNStatusDoneCallback
, &status_done
,
457 XNStatusDrawCallback
, &status_draw
,
459 xic
= XCreateIC( xim
, XNInputStyle
, input_style
, XNPreeditAttributes
, preedit
, XNStatusAttributes
, status
,
460 XNClientWindow
, win
, XNFocusWindow
, win
, XNDestroyCallback
, &destroy
, NULL
);
461 TRACE( "created XIC %p\n", xic
);
469 XIC
X11DRV_get_ic( HWND hwnd
)
471 struct x11drv_win_data
*data
;
475 if (!(data
= get_win_data( hwnd
))) return 0;
476 x11drv_thread_data()->last_xic_hwnd
= hwnd
;
477 if (!(ret
= data
->xic
) && (xim
= x11drv_thread_data()->xim
))
478 ret
= data
->xic
= xic_create( xim
, hwnd
, data
->whole_window
);
479 release_win_data( data
);
484 void xim_set_focus( HWND hwnd
, BOOL focus
)
487 if (!(xic
= X11DRV_get_ic( hwnd
))) return;
488 if (focus
) XSetICFocus( xic
);
489 else XUnsetICFocus( xic
);