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
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
)
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
;
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" );
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
) );
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
)
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
);
136 post_ime_update( hwnd
);
137 x11drv_client_func( client_func_ime_set_result
, output
, len
* sizeof(WCHAR
) );
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
);
152 case XIMPreeditEnable
:
153 send_message( hwnd
, WM_IME_NOTIFY
, IMN_WINE_SET_OPEN_STATUS
, TRUE
);
155 case XIMPreeditDisable
:
156 send_message( hwnd
, WM_IME_NOTIFY
, IMN_WINE_SET_OPEN_STATUS
, FALSE
);
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
);
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
);
187 x11drv_client_call( client_ime_set_composition_status
, FALSE
);
188 post_ime_update( hwnd
);
193 static int xic_preedit_draw( XIC xic
, XPointer user
, XPointer arg
)
195 XIMPreeditDrawCallbackStruct
*params
= (void *)arg
;
196 HWND hwnd
= (HWND
)user
;
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
);
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 );
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
);
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
);
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
;
240 TRACE( "xic %p, hwnd %p, arg %p\n", xic
, hwnd
, arg
);
242 if (!params
) return 0;
245 switch (params
->direction
)
251 case XIMBackwardChar
:
252 case XIMBackwardWord
:
258 case XIMAbsolutePosition
:
259 pos
= params
->position
;
262 params
->position
= pos
;
266 case XIMPreviousLine
:
269 FIXME( "Not implemented\n" );
272 x11drv_client_call( client_ime_set_cursor_pos
, pos
);
273 params
->position
= xim_caret_pos
= pos
;
275 post_ime_update( hwnd
);
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
);
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
);
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
);
301 /***********************************************************************
302 * NotifyIMEStatus (X11DRV.@)
304 void X11DRV_NotifyIMEStatus( HWND hwnd
, UINT status
)
306 XIMPreeditState state
= status
? XIMPreeditEnable
: XIMPreeditDisable
;
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
);
320 if (!status
) XFree( XmbResetIC( xic
) );
323 /***********************************************************************
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");
337 if (XSetLocaleModifiers("") == NULL
)
339 WARN("Could not set locale modifiers.\n");
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
) );
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
;
367 if (!(xim
= XOpenIM( data
->display
, NULL
, NULL
, NULL
)))
369 WARN("Could not open input method.\n");
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
);
382 WARN( "Could not find supported input style.\n" );
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
;
399 if (!input_style
) input_style
= input_style_fallback
;
400 TRACE( "selected style %#lx %s\n", input_style
, debugstr_xim_style( input_style
) );
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;
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
;
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
);
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
};
467 XVaNestedList preedit
, status
;
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
,
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
);
495 XIC
X11DRV_get_ic( HWND hwnd
)
497 struct x11drv_win_data
*data
;
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
);
510 void xim_set_focus( HWND hwnd
, BOOL focus
)
512 struct list updates
= LIST_INIT(updates
);
513 struct ime_update
*update
, *next
;
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
;
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
);