opengl32: Correctly interpret glMapBuffer() access in wow64 mapping.
[wine.git] / dlls / winex11.drv / xim.c
blob209d63f0402b11c9489846144abea1d08adee761
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;
51 DWORD cursor_pos;
52 WCHAR *comp_str;
53 WCHAR *result_str;
54 WCHAR buffer[];
57 static pthread_mutex_t ime_mutex = PTHREAD_MUTEX_INITIALIZER;
58 static struct list ime_updates = LIST_INIT(ime_updates);
59 static DWORD ime_update_count;
60 static WCHAR *ime_comp_buf;
62 static XIMStyle input_style = 0;
63 static XIMStyle input_style_req = XIMPreeditCallbacks | XIMStatusCallbacks;
65 static const char *debugstr_xim_style( XIMStyle style )
67 char buffer[1024], *buf = buffer;
69 buf += sprintf( buf, "preedit" );
70 if (style & XIMPreeditArea) buf += sprintf( buf, " area" );
71 if (style & XIMPreeditCallbacks) buf += sprintf( buf, " callbacks" );
72 if (style & XIMPreeditPosition) buf += sprintf( buf, " position" );
73 if (style & XIMPreeditNothing) buf += sprintf( buf, " nothing" );
74 if (style & XIMPreeditNone) buf += sprintf( buf, " none" );
76 buf += sprintf( buf, ", status" );
77 if (style & XIMStatusArea) buf += sprintf( buf, " area" );
78 if (style & XIMStatusCallbacks) buf += sprintf( buf, " callbacks" );
79 if (style & XIMStatusNothing) buf += sprintf( buf, " nothing" );
80 if (style & XIMStatusNone) buf += sprintf( buf, " none" );
82 return wine_dbg_sprintf( "%s", buffer );
85 BOOL xim_in_compose_mode(void)
87 return !!ime_comp_buf;
90 static void post_ime_update( HWND hwnd, UINT cursor_pos, WCHAR *comp_str, WCHAR *result_str )
92 UINT id, comp_len, result_len;
93 struct ime_update *update;
95 comp_len = comp_str ? wcslen( comp_str ) + 1 : 0;
96 result_len = result_str ? wcslen( result_str ) + 1 : 0;
98 if (!(update = malloc( offsetof(struct ime_update, buffer[comp_len + result_len]) ))) return;
99 update->cursor_pos = cursor_pos;
100 update->comp_str = comp_str ? memcpy( update->buffer, comp_str, comp_len * sizeof(WCHAR) ) : NULL;
101 update->result_str = result_str ? memcpy( update->buffer + comp_len, result_str, result_len * sizeof(WCHAR) ) : NULL;
103 pthread_mutex_lock( &ime_mutex );
104 id = update->id = ++ime_update_count;
105 list_add_tail( &ime_updates, &update->entry );
106 pthread_mutex_unlock( &ime_mutex );
108 NtUserPostMessage( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_COMP_STRING, id );
111 static void xim_update_comp_string( UINT offset, UINT old_len, const WCHAR *text, UINT new_len )
113 UINT len = ime_comp_buf ? wcslen( ime_comp_buf ) : 0;
114 int diff = new_len - old_len;
115 WCHAR *ptr;
117 TRACE( "offset %u, old_len %u, text %s\n", offset, old_len, debugstr_wn(text, new_len) );
119 if (!(ptr = realloc( ime_comp_buf, (len + max(0, diff) + 1) * sizeof(WCHAR) )))
121 ERR( "Failed to reallocate composition string buffer\n" );
122 return;
125 ime_comp_buf = ptr;
126 ptr = ime_comp_buf + offset;
127 memmove( ptr + new_len, ptr + old_len, (len - offset - old_len) * sizeof(WCHAR) );
128 if (text) memcpy( ptr, text, new_len * sizeof(WCHAR) );
129 ime_comp_buf[len + diff] = 0;
132 void xim_set_result_string( HWND hwnd, const char *str, UINT count )
134 WCHAR *output;
135 DWORD len;
137 TRACE( "hwnd %p, string %s\n", hwnd, debugstr_an(str, count) );
139 if (!(output = malloc( (count + 1) * sizeof(WCHAR) ))) return;
140 len = ntdll_umbstowcs( str, count, output, count );
141 output[len] = 0;
143 post_ime_update( hwnd, 0, NULL, output );
145 free( output );
148 static BOOL xic_preedit_state_notify( XIC xic, XPointer user, XPointer arg )
150 XIMPreeditStateNotifyCallbackStruct *params = (void *)arg;
151 const XIMPreeditState state = params->state;
152 HWND hwnd = (HWND)user;
154 TRACE( "xic %p, hwnd %p, state %lu\n", xic, hwnd, state );
156 switch (state)
158 case XIMPreeditEnable:
159 NtUserPostMessage( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, TRUE );
160 break;
161 case XIMPreeditDisable:
162 NtUserPostMessage( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, FALSE );
163 break;
166 return TRUE;
169 static int xic_preedit_start( XIC xic, XPointer user, XPointer arg )
171 HWND hwnd = (HWND)user;
173 TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg );
175 if ((ime_comp_buf = realloc( ime_comp_buf, sizeof(WCHAR) ))) *ime_comp_buf = 0;
176 else ERR( "Failed to allocate preedit buffer\n" );
178 NtUserPostMessage( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, TRUE );
179 post_ime_update( hwnd, 0, ime_comp_buf, NULL );
181 return -1;
184 static int xic_preedit_done( XIC xic, XPointer user, XPointer arg )
186 HWND hwnd = (HWND)user;
188 TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg );
190 free( ime_comp_buf );
191 ime_comp_buf = NULL;
193 post_ime_update( hwnd, 0, NULL, NULL );
194 NtUserPostMessage( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, FALSE );
196 return 0;
199 static int xic_preedit_draw( XIC xic, XPointer user, XPointer arg )
201 XIMPreeditDrawCallbackStruct *params = (void *)arg;
202 HWND hwnd = (HWND)user;
203 size_t text_len;
204 XIMText *text;
205 WCHAR *output;
206 char *str;
207 int len;
209 TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg );
211 if (!params) return 0;
213 if (!(text = params->text)) str = NULL;
214 else if (!text->encoding_is_wchar) str = text->string.multi_byte;
215 else if ((len = wcstombs( NULL, text->string.wide_char, text->length )) < 0) str = NULL;
216 else if ((str = malloc( len + 1 )))
218 wcstombs( str, text->string.wide_char, len );
219 str[len] = 0;
222 if (!str || !(text_len = strlen( str )) || !(output = malloc( text_len * sizeof(WCHAR) )))
223 xim_update_comp_string( params->chg_first, params->chg_length, NULL, 0 );
224 else
226 text_len = ntdll_umbstowcs( str, text_len, output, text_len );
227 xim_update_comp_string( params->chg_first, params->chg_length, output, text_len );
228 free( output );
231 if (text && str != text->string.multi_byte) free( str );
233 post_ime_update( hwnd, params->caret, ime_comp_buf, NULL );
235 return 0;
238 static int xic_preedit_caret( XIC xic, XPointer user, XPointer arg )
240 static int xim_caret_pos;
241 XIMPreeditCaretCallbackStruct *params = (void *)arg;
242 HWND hwnd = (HWND)user;
243 int pos;
245 TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg );
247 if (!params) return 0;
249 pos = xim_caret_pos;
250 switch (params->direction)
252 case XIMForwardChar:
253 case XIMForwardWord:
254 pos++;
255 break;
256 case XIMBackwardChar:
257 case XIMBackwardWord:
258 pos--;
259 break;
260 case XIMLineStart:
261 pos = 0;
262 break;
263 case XIMAbsolutePosition:
264 pos = params->position;
265 break;
266 case XIMDontChange:
267 params->position = pos;
268 return 0;
269 case XIMCaretUp:
270 case XIMCaretDown:
271 case XIMPreviousLine:
272 case XIMNextLine:
273 case XIMLineEnd:
274 FIXME( "Not implemented\n" );
275 break;
277 params->position = xim_caret_pos = pos;
279 post_ime_update( hwnd, pos, ime_comp_buf, NULL );
281 return 0;
284 static int xic_status_start( XIC xic, XPointer user, XPointer arg )
286 HWND hwnd = (HWND)user;
287 TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg );
288 return 0;
291 static int xic_status_done( XIC xic, XPointer user, XPointer arg )
293 HWND hwnd = (HWND)user;
294 TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg );
295 return 0;
298 static int xic_status_draw( XIC xic, XPointer user, XPointer arg )
300 HWND hwnd = (HWND)user;
301 TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg );
302 return 0;
305 /***********************************************************************
306 * NotifyIMEStatus (X11DRV.@)
308 void X11DRV_NotifyIMEStatus( HWND hwnd, UINT status )
310 XIMPreeditState state = status ? XIMPreeditEnable : XIMPreeditDisable;
311 XVaNestedList attr;
312 XIC xic;
314 TRACE( "hwnd %p, status %#x\n", hwnd, status );
316 if (!(xic = X11DRV_get_ic( hwnd ))) return;
318 if ((attr = XVaCreateNestedList( 0, XNPreeditState, state, NULL )))
320 XSetICValues( xic, XNPreeditAttributes, attr, NULL );
321 XFree( attr );
324 if (!status) XFree( XmbResetIC( xic ) );
327 /***********************************************************************
328 * xim_init
330 BOOL xim_init( const WCHAR *input_style )
332 static const WCHAR offthespotW[] = {'o','f','f','t','h','e','s','p','o','t',0};
333 static const WCHAR overthespotW[] = {'o','v','e','r','t','h','e','s','p','o','t',0};
334 static const WCHAR rootW[] = {'r','o','o','t',0};
336 if (!XSupportsLocale())
338 WARN("X does not support locale.\n");
339 return FALSE;
341 if (XSetLocaleModifiers("") == NULL)
343 WARN("Could not set locale modifiers.\n");
344 return FALSE;
347 if (!wcsicmp( input_style, offthespotW ))
348 input_style_req = XIMPreeditArea | XIMStatusArea;
349 else if (!wcsicmp( input_style, overthespotW ))
350 input_style_req = XIMPreeditPosition | XIMStatusNothing;
351 else if (!wcsicmp( input_style, rootW ))
352 input_style_req = XIMPreeditNothing | XIMStatusNothing;
354 TRACE( "requesting %s style %#lx %s\n", debugstr_w(input_style), input_style_req,
355 debugstr_xim_style( input_style_req ) );
357 return TRUE;
360 static void xim_open( Display *display, XPointer user, XPointer arg );
361 static void xim_destroy( XIM xim, XPointer user, XPointer arg );
363 static XIM xim_create( struct x11drv_thread_data *data )
365 XIMCallback destroy = {.callback = xim_destroy, .client_data = (XPointer)data};
366 XIMStyle input_style_fallback = XIMPreeditNone | XIMStatusNone;
367 XIMStyles *styles = NULL;
368 INT i;
369 XIM xim;
371 if (!(xim = XOpenIM( data->display, NULL, NULL, NULL )))
373 WARN("Could not open input method.\n");
374 return NULL;
377 if (XSetIMValues( xim, XNDestroyCallback, &destroy, NULL ))
378 WARN( "Could not set destroy callback.\n" );
380 TRACE( "xim %p, XDisplayOfIM %p, XLocaleOfIM %s\n", xim, XDisplayOfIM( xim ),
381 debugstr_a(XLocaleOfIM( xim )) );
383 XGetIMValues( xim, XNQueryInputStyle, &styles, NULL );
384 if (!styles)
386 WARN( "Could not find supported input style.\n" );
387 XCloseIM( xim );
388 return NULL;
391 TRACE( "input styles count %u\n", styles->count_styles );
392 for (i = 0, input_style = 0; i < styles->count_styles; ++i)
394 XIMStyle style = styles->supported_styles[i];
395 TRACE( " %u: %#lx %s\n", i, style, debugstr_xim_style( style ) );
397 if (style == input_style_req) input_style = style;
398 if (!input_style && (style & input_style_req)) input_style = style;
399 if (input_style_fallback > style) input_style_fallback = style;
401 XFree(styles);
403 if (!input_style) input_style = input_style_fallback;
404 TRACE( "selected style %#lx %s\n", input_style, debugstr_xim_style( input_style ) );
406 return xim;
409 static void xim_open( Display *display, XPointer user, XPointer arg )
411 struct x11drv_thread_data *data = (void *)user;
412 TRACE( "display %p, data %p, arg %p\n", display, user, arg );
413 if (!(data->xim = xim_create( data ))) return;
414 XUnregisterIMInstantiateCallback( display, NULL, NULL, NULL, xim_open, user );
417 static void xim_destroy( XIM xim, XPointer user, XPointer arg )
419 struct x11drv_thread_data *data = x11drv_thread_data();
420 TRACE( "xim %p, user %p, arg %p\n", xim, user, arg );
421 if (data->xim != xim) return;
422 data->xim = NULL;
423 XRegisterIMInstantiateCallback( data->display, NULL, NULL, NULL, xim_open, user );
426 void xim_thread_attach( struct x11drv_thread_data *data )
428 Display *display = data->display;
429 int i, count;
430 char **list;
432 data->font_set = XCreateFontSet( display, "fixed", &list, &count, NULL );
433 TRACE( "created XFontSet %p, list %p, count %d\n", data->font_set, list, count );
434 for (i = 0; list && i < count; ++i) TRACE( " %d: %s\n", i, list[i] );
435 if (list) XFreeStringList( list );
437 if ((data->xim = xim_create( data ))) return;
438 XRegisterIMInstantiateCallback( display, NULL, NULL, NULL, xim_open, (XPointer)data );
441 static BOOL xic_destroy( XIC xic, XPointer user, XPointer arg )
443 struct x11drv_win_data *data;
444 HWND hwnd = (HWND)user;
446 TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg );
448 if ((data = get_win_data( hwnd )))
450 if (data->xic == xic) data->xic = NULL;
451 release_win_data( data );
454 return TRUE;
457 static XIC xic_create( XIM xim, HWND hwnd, Window win )
459 XICCallback destroy = {.callback = xic_destroy, .client_data = (XPointer)hwnd};
460 XICCallback preedit_caret = {.callback = xic_preedit_caret, .client_data = (XPointer)hwnd};
461 XICCallback preedit_done = {.callback = xic_preedit_done, .client_data = (XPointer)hwnd};
462 XICCallback preedit_draw = {.callback = xic_preedit_draw, .client_data = (XPointer)hwnd};
463 XICCallback preedit_start = {.callback = xic_preedit_start, .client_data = (XPointer)hwnd};
464 XICCallback preedit_state_notify = {.callback = xic_preedit_state_notify, .client_data = (XPointer)hwnd};
465 XICCallback status_done = {.callback = xic_status_done, .client_data = (XPointer)hwnd};
466 XICCallback status_draw = {.callback = xic_status_draw, .client_data = (XPointer)hwnd};
467 XICCallback status_start = {.callback = xic_status_start, .client_data = (XPointer)hwnd};
468 XPoint spot = {0};
469 XVaNestedList preedit, status;
470 XIC xic;
471 XFontSet fontSet = x11drv_thread_data()->font_set;
473 TRACE( "xim %p, hwnd %p/%lx\n", xim, hwnd, win );
475 preedit = XVaCreateNestedList( 0, XNFontSet, fontSet,
476 XNPreeditCaretCallback, &preedit_caret,
477 XNPreeditDoneCallback, &preedit_done,
478 XNPreeditDrawCallback, &preedit_draw,
479 XNPreeditStartCallback, &preedit_start,
480 XNPreeditStateNotifyCallback, &preedit_state_notify,
481 XNSpotLocation, &spot, NULL );
482 status = XVaCreateNestedList( 0, XNFontSet, fontSet,
483 XNStatusStartCallback, &status_start,
484 XNStatusDoneCallback, &status_done,
485 XNStatusDrawCallback, &status_draw,
486 NULL );
487 xic = XCreateIC( xim, XNInputStyle, input_style, XNPreeditAttributes, preedit, XNStatusAttributes, status,
488 XNClientWindow, win, XNFocusWindow, win, XNDestroyCallback, &destroy, NULL );
489 TRACE( "created XIC %p\n", xic );
491 XFree( preedit );
492 XFree( status );
494 return xic;
497 XIC X11DRV_get_ic( HWND hwnd )
499 struct x11drv_win_data *data;
500 XIM xim;
501 XIC ret;
503 if (!(data = get_win_data( hwnd ))) return 0;
504 x11drv_thread_data()->last_xic_hwnd = hwnd;
505 if (!(ret = data->xic) && (xim = x11drv_thread_data()->xim))
506 ret = data->xic = xic_create( xim, hwnd, data->whole_window );
507 release_win_data( data );
509 return ret;
512 void xim_set_focus( HWND hwnd, BOOL focus )
514 struct list updates = LIST_INIT(updates);
515 struct ime_update *update, *next;
516 XIC xic;
518 if (!(xic = X11DRV_get_ic( hwnd ))) return;
520 if (focus) XSetICFocus( xic );
521 else XUnsetICFocus( xic );
523 pthread_mutex_lock( &ime_mutex );
524 list_move_tail( &updates, &ime_updates );
525 pthread_mutex_unlock( &ime_mutex );
527 LIST_FOR_EACH_ENTRY_SAFE( update, next, &updates, struct ime_update, entry ) free( update );
530 static struct ime_update *find_ime_update( UINT id )
532 struct ime_update *update;
533 LIST_FOR_EACH_ENTRY( update, &ime_updates, struct ime_update, entry )
534 if (update->id == id) return update;
535 return NULL;
538 /***********************************************************************
539 * ImeToAsciiEx (X11DRV.@)
541 * As XIM filters key events upfront, we don't use ImeProcessKey and ImeToAsciiEx is instead called
542 * back from the IME UI window procedure when WM_IME_NOTIFY / IMN_WINE_SET_COMP_STRING messages are
543 * sent to it, to retrieve composition string updates and generate WM_IME messages.
545 UINT X11DRV_ImeToAsciiEx( UINT vkey, UINT lparam, const BYTE *state, COMPOSITIONSTRING *compstr, HIMC himc )
547 UINT needed = sizeof(COMPOSITIONSTRING), comp_len, result_len;
548 struct ime_update *update;
549 void *dst;
551 TRACE( "vkey %#x, lparam %#x, state %p, compstr %p, himc %p\n", vkey, lparam, state, compstr, himc );
553 pthread_mutex_lock( &ime_mutex );
555 if (!(update = find_ime_update( lparam )))
557 pthread_mutex_unlock( &ime_mutex );
558 return 0;
561 if (!update->comp_str) comp_len = 0;
562 else
564 comp_len = wcslen( update->comp_str );
565 needed += comp_len * sizeof(WCHAR); /* GCS_COMPSTR */
566 needed += comp_len; /* GCS_COMPATTR */
567 needed += 2 * sizeof(DWORD); /* GCS_COMPCLAUSE */
570 if (!update->result_str) result_len = 0;
571 else
573 result_len = wcslen( update->result_str );
574 needed += result_len * sizeof(WCHAR); /* GCS_RESULTSTR */
575 needed += 2 * sizeof(DWORD); /* GCS_RESULTCLAUSE */
578 if (compstr->dwSize < needed)
580 compstr->dwSize = needed;
581 pthread_mutex_unlock( &ime_mutex );
582 return STATUS_BUFFER_TOO_SMALL;
585 list_remove( &update->entry );
586 pthread_mutex_unlock( &ime_mutex );
588 memset( compstr, 0, sizeof(*compstr) );
589 compstr->dwSize = sizeof(*compstr);
591 if (update->comp_str)
593 compstr->dwCursorPos = update->cursor_pos;
595 compstr->dwCompStrLen = comp_len;
596 compstr->dwCompStrOffset = compstr->dwSize;
597 dst = (BYTE *)compstr + compstr->dwCompStrOffset;
598 memcpy( dst, update->comp_str, compstr->dwCompStrLen * sizeof(WCHAR) );
599 compstr->dwSize += compstr->dwCompStrLen * sizeof(WCHAR);
601 compstr->dwCompClauseLen = 2 * sizeof(DWORD);
602 compstr->dwCompClauseOffset = compstr->dwSize;
603 dst = (BYTE *)compstr + compstr->dwCompClauseOffset;
604 *((DWORD *)dst + 0) = 0;
605 *((DWORD *)dst + 1) = compstr->dwCompStrLen;
606 compstr->dwSize += compstr->dwCompClauseLen;
608 compstr->dwCompAttrLen = compstr->dwCompStrLen;
609 compstr->dwCompAttrOffset = compstr->dwSize;
610 dst = (BYTE *)compstr + compstr->dwCompAttrOffset;
611 memset( dst, ATTR_INPUT, compstr->dwCompAttrLen );
612 compstr->dwSize += compstr->dwCompAttrLen;
615 if (update->result_str)
617 compstr->dwResultStrLen = result_len;
618 compstr->dwResultStrOffset = compstr->dwSize;
619 dst = (BYTE *)compstr + compstr->dwResultStrOffset;
620 memcpy( dst, update->result_str, compstr->dwResultStrLen * sizeof(WCHAR) );
621 compstr->dwSize += compstr->dwResultStrLen * sizeof(WCHAR);
623 compstr->dwResultClauseLen = 2 * sizeof(DWORD);
624 compstr->dwResultClauseOffset = compstr->dwSize;
625 dst = (BYTE *)compstr + compstr->dwResultClauseOffset;
626 *((DWORD *)dst + 0) = 0;
627 *((DWORD *)dst + 1) = compstr->dwResultStrLen;
628 compstr->dwSize += compstr->dwResultClauseLen;
631 free( update );
632 return 0;