include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / winex11.drv / xim.c
blobc6a93eb5e1655b01eb6e3bb7664c214f7bd3a408
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 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;
87 WCHAR *ptr;
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" );
94 return;
97 ime_comp_buf = ptr;
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 )
106 WCHAR *output;
107 DWORD len;
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 );
113 output[len] = 0;
115 post_ime_update( hwnd, 0, NULL, output );
117 free( 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 );
128 switch (state)
130 case XIMPreeditEnable:
131 NtUserPostMessage( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, TRUE );
132 break;
133 case XIMPreeditDisable:
134 NtUserPostMessage( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, FALSE );
135 break;
138 return TRUE;
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 );
153 return -1;
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 );
163 ime_comp_buf = NULL;
165 post_ime_update( hwnd, 0, NULL, NULL );
166 NtUserPostMessage( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, FALSE );
168 return 0;
171 static int xic_preedit_draw( XIC xic, XPointer user, XPointer arg )
173 XIMPreeditDrawCallbackStruct *params = (void *)arg;
174 HWND hwnd = (HWND)user;
175 size_t text_len;
176 XIMText *text;
177 WCHAR *output;
178 char *str;
179 int len;
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 );
191 str[len] = 0;
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 );
196 else
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 );
200 free( output );
203 if (text && str != text->string.multi_byte) free( str );
205 post_ime_update( hwnd, params->caret, ime_comp_buf, NULL );
207 return 0;
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;
215 int pos;
217 TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg );
219 if (!params) return 0;
221 pos = xim_caret_pos;
222 switch (params->direction)
224 case XIMForwardChar:
225 case XIMForwardWord:
226 pos++;
227 break;
228 case XIMBackwardChar:
229 case XIMBackwardWord:
230 pos--;
231 break;
232 case XIMLineStart:
233 pos = 0;
234 break;
235 case XIMAbsolutePosition:
236 pos = params->position;
237 break;
238 case XIMDontChange:
239 params->position = pos;
240 return 0;
241 case XIMCaretUp:
242 case XIMCaretDown:
243 case XIMPreviousLine:
244 case XIMNextLine:
245 case XIMLineEnd:
246 FIXME( "Not implemented\n" );
247 break;
249 params->position = xim_caret_pos = pos;
251 post_ime_update( hwnd, pos, ime_comp_buf, NULL );
253 return 0;
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 );
260 return 0;
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 );
267 return 0;
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 );
274 return 0;
277 /***********************************************************************
278 * NotifyIMEStatus (X11DRV.@)
280 void X11DRV_NotifyIMEStatus( HWND hwnd, UINT status )
282 XIMPreeditState state = status ? XIMPreeditEnable : XIMPreeditDisable;
283 XVaNestedList attr;
284 XIC xic;
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 );
293 XFree( attr );
296 if (!status) XFree( XmbResetIC( xic ) );
299 /***********************************************************************
300 * xim_init
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");
311 return FALSE;
313 if (XSetLocaleModifiers("") == NULL)
315 WARN("Could not set locale modifiers.\n");
316 return FALSE;
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 ) );
329 return TRUE;
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;
340 INT i;
341 XIM xim;
343 if (!(xim = XOpenIM( data->display, NULL, NULL, NULL )))
345 WARN("Could not open input method.\n");
346 return NULL;
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 );
356 if (!styles)
358 WARN( "Could not find supported input style.\n" );
359 XCloseIM( xim );
360 return NULL;
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;
373 XFree(styles);
375 if (!input_style) input_style = input_style_fallback;
376 TRACE( "selected style %#lx %s\n", input_style, debugstr_xim_style( input_style ) );
378 return xim;
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;
394 data->xim = NULL;
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;
401 int i, count;
402 char **list;
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 );
426 return TRUE;
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};
440 XPoint spot = {0};
441 XVaNestedList preedit, status;
442 XIC xic;
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,
458 NULL );
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 );
463 XFree( preedit );
464 XFree( status );
466 return xic;
469 XIC X11DRV_get_ic( HWND hwnd )
471 struct x11drv_win_data *data;
472 XIM xim;
473 XIC ret;
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 );
481 return ret;
484 void xim_set_focus( HWND hwnd, BOOL focus )
486 XIC xic;
487 if (!(xic = X11DRV_get_ic( hwnd ))) return;
488 if (focus) XSetICFocus( xic );
489 else XUnsetICFocus( xic );