winex11: Post internal WM_IME_NOTIFY wparam on composition updates.
[wine.git] / dlls / winex11.drv / xim.c
blob1dde1fc936cc35728331fc5970cf96c83d1cd1d9
1 /*
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
21 #if 0
22 #pragma makedep unix
23 #endif
25 #include "config.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
30 #include "ntstatus.h"
31 #define WIN32_NO_STATUS
32 #include "windef.h"
33 #include "winbase.h"
34 #include "winnls.h"
35 #include "winternl.h"
36 #include "x11drv.h"
37 #include "imm.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(xim);
42 #ifndef HAVE_XICCALLBACK_CALLBACK
43 #define XICCallback XIMCallback
44 #define XICProc XIMProc
45 #endif
47 struct ime_update
49 struct list entry;
50 DWORD id;
53 static pthread_mutex_t ime_mutex = PTHREAD_MUTEX_INITIALIZER;
54 static struct list ime_updates = LIST_INIT(ime_updates);
55 static DWORD ime_update_count;
56 static WCHAR *ime_comp_buf;
58 static XIMStyle input_style = 0;
59 static XIMStyle input_style_req = XIMPreeditCallbacks | XIMStatusCallbacks;
61 static const char *debugstr_xim_style( XIMStyle style )
63 char buffer[1024], *buf = buffer;
65 buf += sprintf( buf, "preedit" );
66 if (style & XIMPreeditArea) buf += sprintf( buf, " area" );
67 if (style & XIMPreeditCallbacks) buf += sprintf( buf, " callbacks" );
68 if (style & XIMPreeditPosition) buf += sprintf( buf, " position" );
69 if (style & XIMPreeditNothing) buf += sprintf( buf, " nothing" );
70 if (style & XIMPreeditNone) buf += sprintf( buf, " none" );
72 buf += sprintf( buf, ", status" );
73 if (style & XIMStatusArea) buf += sprintf( buf, " area" );
74 if (style & XIMStatusCallbacks) buf += sprintf( buf, " callbacks" );
75 if (style & XIMStatusNothing) buf += sprintf( buf, " nothing" );
76 if (style & XIMStatusNone) buf += sprintf( buf, " none" );
78 return wine_dbg_sprintf( "%s", buffer );
81 BOOL xim_in_compose_mode(void)
83 return !!ime_comp_buf;
86 static void post_ime_update( HWND hwnd )
88 UINT id;
89 struct ime_update *update;
91 if (!(update = malloc( sizeof(struct ime_update) ))) return;
93 pthread_mutex_lock( &ime_mutex );
94 id = update->id = ++ime_update_count;
95 list_add_tail( &ime_updates, &update->entry );
96 pthread_mutex_unlock( &ime_mutex );
98 NtUserPostMessage( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_COMP_STRING, id );
101 static void xim_update_comp_string( UINT offset, UINT old_len, const WCHAR *text, UINT new_len )
103 UINT len = ime_comp_buf ? wcslen( ime_comp_buf ) : 0;
104 int diff = new_len - old_len;
105 WCHAR *ptr;
107 TRACE( "offset %u, old_len %u, text %s\n", offset, old_len, debugstr_wn(text, new_len) );
109 if (!(ptr = realloc( ime_comp_buf, (len + max(0, diff) + 1) * sizeof(WCHAR) )))
111 ERR( "Failed to reallocate composition string buffer\n" );
112 return;
115 ime_comp_buf = ptr;
116 ptr = ime_comp_buf + offset;
117 memmove( ptr + new_len, ptr + old_len, (len - offset - old_len) * sizeof(WCHAR) );
118 if (text) memcpy( ptr, text, new_len * sizeof(WCHAR) );
119 len += diff;
120 ime_comp_buf[len] = 0;
122 x11drv_client_func( client_func_ime_set_composition_string, ime_comp_buf, len * sizeof(WCHAR) );
125 void xim_set_result_string( HWND hwnd, const char *str, UINT count )
127 WCHAR *output;
128 DWORD len;
130 TRACE( "hwnd %p, string %s\n", hwnd, debugstr_an(str, count) );
132 if (!(output = malloc( (count + 1) * sizeof(WCHAR) ))) return;
133 len = ntdll_umbstowcs( str, count, output, count );
134 output[len] = 0;
136 post_ime_update( hwnd );
137 x11drv_client_func( client_func_ime_set_result, output, len * sizeof(WCHAR) );
139 free( output );
142 static BOOL xic_preedit_state_notify( XIC xic, XPointer user, XPointer arg )
144 XIMPreeditStateNotifyCallbackStruct *params = (void *)arg;
145 const XIMPreeditState state = params->state;
146 HWND hwnd = (HWND)user;
148 TRACE( "xic %p, hwnd %p, state %lu\n", xic, hwnd, state );
150 switch (state)
152 case XIMPreeditEnable:
153 send_message( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, TRUE );
154 break;
155 case XIMPreeditDisable:
156 send_message( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, FALSE );
157 break;
160 return TRUE;
163 static int xic_preedit_start( XIC xic, XPointer user, XPointer arg )
165 HWND hwnd = (HWND)user;
167 TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg );
169 x11drv_client_call( client_ime_set_composition_status, TRUE );
170 if ((ime_comp_buf = realloc( ime_comp_buf, sizeof(WCHAR) ))) *ime_comp_buf = 0;
171 else ERR( "Failed to allocate preedit buffer\n" );
173 post_ime_update( hwnd );
175 return -1;
178 static int xic_preedit_done( XIC xic, XPointer user, XPointer arg )
180 HWND hwnd = (HWND)user;
182 TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg );
184 free( ime_comp_buf );
185 ime_comp_buf = NULL;
187 x11drv_client_call( client_ime_set_composition_status, FALSE );
188 post_ime_update( hwnd );
190 return 0;
193 static int xic_preedit_draw( XIC xic, XPointer user, XPointer arg )
195 XIMPreeditDrawCallbackStruct *params = (void *)arg;
196 HWND hwnd = (HWND)user;
197 size_t text_len;
198 XIMText *text;
199 WCHAR *output;
200 char *str;
201 int len;
203 TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg );
205 if (!params) return 0;
207 if (!(text = params->text)) str = NULL;
208 else if (!text->encoding_is_wchar) str = text->string.multi_byte;
209 else if ((len = wcstombs( NULL, text->string.wide_char, text->length )) < 0) str = NULL;
210 else if ((str = malloc( len + 1 )))
212 wcstombs( str, text->string.wide_char, len );
213 str[len] = 0;
216 if (!str || !(text_len = strlen( str )) || !(output = malloc( text_len * sizeof(WCHAR) )))
217 xim_update_comp_string( params->chg_first, params->chg_length, NULL, 0 );
218 else
220 text_len = ntdll_umbstowcs( str, text_len, output, text_len );
221 xim_update_comp_string( params->chg_first, params->chg_length, output, text_len );
222 free( output );
225 if (text && str != text->string.multi_byte) free( str );
227 x11drv_client_call( client_ime_set_cursor_pos, params->caret );
228 post_ime_update( hwnd );
230 return 0;
233 static int xic_preedit_caret( XIC xic, XPointer user, XPointer arg )
235 static int xim_caret_pos;
236 XIMPreeditCaretCallbackStruct *params = (void *)arg;
237 HWND hwnd = (HWND)user;
238 int pos;
240 TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg );
242 if (!params) return 0;
244 pos = xim_caret_pos;
245 switch (params->direction)
247 case XIMForwardChar:
248 case XIMForwardWord:
249 pos++;
250 break;
251 case XIMBackwardChar:
252 case XIMBackwardWord:
253 pos--;
254 break;
255 case XIMLineStart:
256 pos = 0;
257 break;
258 case XIMAbsolutePosition:
259 pos = params->position;
260 break;
261 case XIMDontChange:
262 params->position = pos;
263 return 0;
264 case XIMCaretUp:
265 case XIMCaretDown:
266 case XIMPreviousLine:
267 case XIMNextLine:
268 case XIMLineEnd:
269 FIXME( "Not implemented\n" );
270 break;
272 x11drv_client_call( client_ime_set_cursor_pos, pos );
273 params->position = xim_caret_pos = pos;
275 post_ime_update( hwnd );
277 return 0;
280 static int xic_status_start( XIC xic, XPointer user, XPointer arg )
282 HWND hwnd = (HWND)user;
283 TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg );
284 return 0;
287 static int xic_status_done( XIC xic, XPointer user, XPointer arg )
289 HWND hwnd = (HWND)user;
290 TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg );
291 return 0;
294 static int xic_status_draw( XIC xic, XPointer user, XPointer arg )
296 HWND hwnd = (HWND)user;
297 TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg );
298 return 0;
301 /***********************************************************************
302 * NotifyIMEStatus (X11DRV.@)
304 void X11DRV_NotifyIMEStatus( HWND hwnd, UINT status )
306 XIMPreeditState state = status ? XIMPreeditEnable : XIMPreeditDisable;
307 XVaNestedList attr;
308 XIC xic;
310 TRACE( "hwnd %p, status %#x\n", hwnd, status );
312 if (!(xic = X11DRV_get_ic( hwnd ))) return;
314 if ((attr = XVaCreateNestedList( 0, XNPreeditState, state, NULL )))
316 XSetICValues( xic, XNPreeditAttributes, attr, NULL );
317 XFree( attr );
320 if (!status) XFree( XmbResetIC( xic ) );
323 /***********************************************************************
324 * xim_init
326 BOOL xim_init( const WCHAR *input_style )
328 static const WCHAR offthespotW[] = {'o','f','f','t','h','e','s','p','o','t',0};
329 static const WCHAR overthespotW[] = {'o','v','e','r','t','h','e','s','p','o','t',0};
330 static const WCHAR rootW[] = {'r','o','o','t',0};
332 if (!XSupportsLocale())
334 WARN("X does not support locale.\n");
335 return FALSE;
337 if (XSetLocaleModifiers("") == NULL)
339 WARN("Could not set locale modifiers.\n");
340 return FALSE;
343 if (!wcsicmp( input_style, offthespotW ))
344 input_style_req = XIMPreeditArea | XIMStatusArea;
345 else if (!wcsicmp( input_style, overthespotW ))
346 input_style_req = XIMPreeditPosition | XIMStatusNothing;
347 else if (!wcsicmp( input_style, rootW ))
348 input_style_req = XIMPreeditNothing | XIMStatusNothing;
350 TRACE( "requesting %s style %#lx %s\n", debugstr_w(input_style), input_style_req,
351 debugstr_xim_style( input_style_req ) );
353 return TRUE;
356 static void xim_open( Display *display, XPointer user, XPointer arg );
357 static void xim_destroy( XIM xim, XPointer user, XPointer arg );
359 static XIM xim_create( struct x11drv_thread_data *data )
361 XIMCallback destroy = {.callback = xim_destroy, .client_data = (XPointer)data};
362 XIMStyle input_style_fallback = XIMPreeditNone | XIMStatusNone;
363 XIMStyles *styles = NULL;
364 INT i;
365 XIM xim;
367 if (!(xim = XOpenIM( data->display, NULL, NULL, NULL )))
369 WARN("Could not open input method.\n");
370 return NULL;
373 if (XSetIMValues( xim, XNDestroyCallback, &destroy, NULL ))
374 WARN( "Could not set destroy callback.\n" );
376 TRACE( "xim %p, XDisplayOfIM %p, XLocaleOfIM %s\n", xim, XDisplayOfIM( xim ),
377 debugstr_a(XLocaleOfIM( xim )) );
379 XGetIMValues( xim, XNQueryInputStyle, &styles, NULL );
380 if (!styles)
382 WARN( "Could not find supported input style.\n" );
383 XCloseIM( xim );
384 return NULL;
387 TRACE( "input styles count %u\n", styles->count_styles );
388 for (i = 0, input_style = 0; i < styles->count_styles; ++i)
390 XIMStyle style = styles->supported_styles[i];
391 TRACE( " %u: %#lx %s\n", i, style, debugstr_xim_style( style ) );
393 if (style == input_style_req) input_style = style;
394 if (!input_style && (style & input_style_req)) input_style = style;
395 if (input_style_fallback > style) input_style_fallback = style;
397 XFree(styles);
399 if (!input_style) input_style = input_style_fallback;
400 TRACE( "selected style %#lx %s\n", input_style, debugstr_xim_style( input_style ) );
402 return xim;
405 static void xim_open( Display *display, XPointer user, XPointer arg )
407 struct x11drv_thread_data *data = (void *)user;
408 TRACE( "display %p, data %p, arg %p\n", display, user, arg );
409 if (!(data->xim = xim_create( data ))) return;
410 XUnregisterIMInstantiateCallback( display, NULL, NULL, NULL, xim_open, user );
412 x11drv_client_call( client_ime_update_association, 0 );
415 static void xim_destroy( XIM xim, XPointer user, XPointer arg )
417 struct x11drv_thread_data *data = x11drv_thread_data();
418 TRACE( "xim %p, user %p, arg %p\n", xim, user, arg );
419 if (data->xim != xim) return;
420 data->xim = NULL;
421 XRegisterIMInstantiateCallback( data->display, NULL, NULL, NULL, xim_open, user );
424 void xim_thread_attach( struct x11drv_thread_data *data )
426 Display *display = data->display;
427 int i, count;
428 char **list;
430 data->font_set = XCreateFontSet( display, "fixed", &list, &count, NULL );
431 TRACE( "created XFontSet %p, list %p, count %d\n", data->font_set, list, count );
432 for (i = 0; list && i < count; ++i) TRACE( " %d: %s\n", i, list[i] );
433 if (list) XFreeStringList( list );
435 if ((data->xim = xim_create( data ))) x11drv_client_call( client_ime_update_association, 0 );
436 else XRegisterIMInstantiateCallback( display, NULL, NULL, NULL, xim_open, (XPointer)data );
439 static BOOL xic_destroy( XIC xic, XPointer user, XPointer arg )
441 struct x11drv_win_data *data;
442 HWND hwnd = (HWND)user;
444 TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg );
446 if ((data = get_win_data( hwnd )))
448 if (data->xic == xic) data->xic = NULL;
449 release_win_data( data );
452 return TRUE;
455 static XIC xic_create( XIM xim, HWND hwnd, Window win )
457 XICCallback destroy = {.callback = xic_destroy, .client_data = (XPointer)hwnd};
458 XICCallback preedit_caret = {.callback = xic_preedit_caret, .client_data = (XPointer)hwnd};
459 XICCallback preedit_done = {.callback = xic_preedit_done, .client_data = (XPointer)hwnd};
460 XICCallback preedit_draw = {.callback = xic_preedit_draw, .client_data = (XPointer)hwnd};
461 XICCallback preedit_start = {.callback = xic_preedit_start, .client_data = (XPointer)hwnd};
462 XICCallback preedit_state_notify = {.callback = xic_preedit_state_notify, .client_data = (XPointer)hwnd};
463 XICCallback status_done = {.callback = xic_status_done, .client_data = (XPointer)hwnd};
464 XICCallback status_draw = {.callback = xic_status_draw, .client_data = (XPointer)hwnd};
465 XICCallback status_start = {.callback = xic_status_start, .client_data = (XPointer)hwnd};
466 XPoint spot = {0};
467 XVaNestedList preedit, status;
468 XIC xic;
469 XFontSet fontSet = x11drv_thread_data()->font_set;
471 TRACE( "xim %p, hwnd %p/%lx\n", xim, hwnd, win );
473 preedit = XVaCreateNestedList( 0, XNFontSet, fontSet,
474 XNPreeditCaretCallback, &preedit_caret,
475 XNPreeditDoneCallback, &preedit_done,
476 XNPreeditDrawCallback, &preedit_draw,
477 XNPreeditStartCallback, &preedit_start,
478 XNPreeditStateNotifyCallback, &preedit_state_notify,
479 XNSpotLocation, &spot, NULL );
480 status = XVaCreateNestedList( 0, XNFontSet, fontSet,
481 XNStatusStartCallback, &status_start,
482 XNStatusDoneCallback, &status_done,
483 XNStatusDrawCallback, &status_draw,
484 NULL );
485 xic = XCreateIC( xim, XNInputStyle, input_style, XNPreeditAttributes, preedit, XNStatusAttributes, status,
486 XNClientWindow, win, XNFocusWindow, win, XNDestroyCallback, &destroy, NULL );
487 TRACE( "created XIC %p\n", xic );
489 XFree( preedit );
490 XFree( status );
492 return xic;
495 XIC X11DRV_get_ic( HWND hwnd )
497 struct x11drv_win_data *data;
498 XIM xim;
499 XIC ret;
501 if (!(data = get_win_data( hwnd ))) return 0;
502 x11drv_thread_data()->last_xic_hwnd = hwnd;
503 if (!(ret = data->xic) && (xim = x11drv_thread_data()->xim))
504 ret = data->xic = xic_create( xim, hwnd, data->whole_window );
505 release_win_data( data );
507 return ret;
510 void xim_set_focus( HWND hwnd, BOOL focus )
512 struct list updates = LIST_INIT(updates);
513 struct ime_update *update, *next;
514 XIC xic;
516 if (!(xic = X11DRV_get_ic( hwnd ))) return;
518 if (focus) XSetICFocus( xic );
519 else XUnsetICFocus( xic );
521 pthread_mutex_lock( &ime_mutex );
522 list_move_tail( &updates, &ime_updates );
523 pthread_mutex_unlock( &ime_mutex );
525 LIST_FOR_EACH_ENTRY_SAFE( update, next, &updates, struct ime_update, entry ) free( update );
528 static struct ime_update *find_ime_update( UINT id )
530 struct ime_update *update;
531 LIST_FOR_EACH_ENTRY( update, &ime_updates, struct ime_update, entry )
532 if (update->id == id) return update;
533 return NULL;
536 /***********************************************************************
537 * ImeToAsciiEx (X11DRV.@)
539 UINT X11DRV_ImeToAsciiEx( UINT vkey, UINT lparam, const BYTE *state, COMPOSITIONSTRING *compstr, HIMC himc )
541 struct ime_update *update;
543 TRACE( "vkey %#x, lparam %#x, state %p, compstr %p, himc %p\n", vkey, lparam, state, compstr, himc );
545 pthread_mutex_lock( &ime_mutex );
547 if ((update = find_ime_update( lparam )))
548 list_remove( &update->entry );
550 pthread_mutex_unlock( &ime_mutex );
552 free( update );
553 return 0;