2 * Input Context implementation
4 * Copyright 1998 Patrik Stridvall
5 * Copyright 2002, 2003, 2007 CodeWeavers, Aric Stewart
6 * Copyright 2022 Jacek Caban for CodeWeavers
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #define WIN32_NO_STATUS
30 #include "win32u_private.h"
31 #include "ntuser_private.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(imm
);
40 struct user_object obj
;
45 struct imm_thread_data
54 static struct list thread_data_list
= LIST_INIT( thread_data_list
);
55 static pthread_mutex_t imm_mutex
= PTHREAD_MUTEX_INITIALIZER
;
56 static BOOL disable_ime
;
58 static struct imc
*get_imc_ptr( HIMC handle
)
60 struct imc
*imc
= get_user_handle_ptr( handle
, NTUSER_OBJ_IMC
);
61 if (imc
&& imc
!= OBJ_OTHER_PROCESS
) return imc
;
62 WARN( "invalid handle %p\n", handle
);
63 RtlSetLastWin32Error( ERROR_INVALID_HANDLE
);
67 static void release_imc_ptr( struct imc
*imc
)
69 release_user_handle_ptr( imc
);
72 /******************************************************************************
73 * NtUserCreateInputContext (win32u.@)
75 HIMC WINAPI
NtUserCreateInputContext( UINT_PTR client_ptr
)
80 if (!(imc
= malloc( sizeof(*imc
) ))) return 0;
81 imc
->client_ptr
= client_ptr
;
82 imc
->thread_id
= GetCurrentThreadId();
83 if (!(handle
= alloc_user_handle( &imc
->obj
, NTUSER_OBJ_IMC
)))
89 TRACE( "%lx returning %p\n", (long)client_ptr
, handle
);
93 /******************************************************************************
94 * NtUserDestroyInputContext (win32u.@)
96 BOOL WINAPI
NtUserDestroyInputContext( HIMC handle
)
100 TRACE( "%p\n", handle
);
102 if (!(imc
= free_user_handle( handle
, NTUSER_OBJ_IMC
))) return FALSE
;
103 if (imc
== OBJ_OTHER_PROCESS
)
105 FIXME( "other process handle %p\n", handle
);
112 /******************************************************************************
113 * NtUserUpdateInputContext (win32u.@)
115 BOOL WINAPI
NtUserUpdateInputContext( HIMC handle
, UINT attr
, UINT_PTR value
)
120 TRACE( "%p %u %lx\n", handle
, attr
, (long)value
);
122 if (!(imc
= get_imc_ptr( handle
))) return FALSE
;
126 case NtUserInputContextClientPtr
:
127 imc
->client_ptr
= value
;
131 FIXME( "unknown attr %u\n", attr
);
135 release_imc_ptr( imc
);
139 /******************************************************************************
140 * NtUserQueryInputContext (win32u.@)
142 UINT_PTR WINAPI
NtUserQueryInputContext( HIMC handle
, UINT attr
)
147 if (!(imc
= get_imc_ptr( handle
))) return FALSE
;
151 case NtUserInputContextClientPtr
:
152 ret
= imc
->client_ptr
;
155 case NtUserInputContextThreadId
:
156 ret
= imc
->thread_id
;
160 FIXME( "unknown attr %u\n", attr
);
164 release_imc_ptr( imc
);
168 /******************************************************************************
169 * NtUserAssociateInputContext (win32u.@)
171 UINT WINAPI
NtUserAssociateInputContext( HWND hwnd
, HIMC ctx
, ULONG flags
)
176 TRACE( "%p %p %x\n", hwnd
, ctx
, (int)flags
);
181 case IACE_IGNORENOCONTEXT
:
186 FIXME( "unknown flags 0x%x\n", (int)flags
);
190 if (flags
== IACE_DEFAULT
)
192 if (!(ctx
= get_default_input_context())) return AICR_FAILED
;
196 if (NtUserQueryInputContext( ctx
, NtUserInputContextThreadId
) != GetCurrentThreadId())
200 if (!(win
= get_win_ptr( hwnd
)) || win
== WND_OTHER_PROCESS
|| win
== WND_DESKTOP
)
203 if (ctx
&& win
->tid
!= GetCurrentThreadId()) ret
= AICR_FAILED
;
204 else if (flags
!= IACE_IGNORENOCONTEXT
|| win
->imc
)
206 if (win
->imc
!= ctx
&& get_focus() == hwnd
) ret
= AICR_FOCUS_CHANGED
;
210 release_win_ptr( win
);
214 HIMC
get_default_input_context(void)
216 struct ntuser_thread_info
*thread_info
= NtUserGetThreadInfo();
217 if (!thread_info
->default_imc
)
218 thread_info
->default_imc
= HandleToUlong( NtUserCreateInputContext( 0 ));
219 return UlongToHandle( thread_info
->default_imc
);
222 HIMC
get_window_input_context( HWND hwnd
)
227 if (!(win
= get_win_ptr( hwnd
)) || win
== WND_OTHER_PROCESS
|| win
== WND_DESKTOP
)
229 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE
);
234 release_win_ptr( win
);
238 static HWND
detach_default_window( struct imm_thread_data
*thread_data
)
240 HWND hwnd
= thread_data
->default_hwnd
;
241 thread_data
->default_hwnd
= NULL
;
242 thread_data
->window_cnt
= 0;
246 static struct imm_thread_data
*get_imm_thread_data(void)
248 struct user_thread_info
*thread_info
= get_user_thread_info();
249 if (!thread_info
->imm_thread_data
)
251 struct imm_thread_data
*data
;
252 if (!(data
= calloc( 1, sizeof( *data
)))) return NULL
;
253 data
->thread_id
= GetCurrentThreadId();
255 pthread_mutex_lock( &imm_mutex
);
256 list_add_tail( &thread_data_list
, &data
->entry
);
257 pthread_mutex_unlock( &imm_mutex
);
259 thread_info
->imm_thread_data
= data
;
261 return thread_info
->imm_thread_data
;
264 BOOL
register_imm_window( HWND hwnd
)
266 struct imm_thread_data
*thread_data
;
268 TRACE( "(%p)\n", hwnd
);
270 if (disable_ime
|| !needs_ime_window( hwnd
))
273 thread_data
= get_imm_thread_data();
274 if (!thread_data
|| thread_data
->disable_ime
)
277 TRACE( "window_cnt=%u, default_hwnd=%p\n", thread_data
->window_cnt
+ 1, thread_data
->default_hwnd
);
279 /* Create default IME window */
280 if (!thread_data
->window_cnt
++)
282 static const WCHAR imeW
[] = {'I','M','E',0};
283 static const WCHAR default_imeW
[] = {'D','e','f','a','u','l','t',' ','I','M','E',0};
284 UNICODE_STRING class_name
= RTL_CONSTANT_STRING( imeW
);
285 UNICODE_STRING name
= RTL_CONSTANT_STRING( default_imeW
);
287 thread_data
->default_hwnd
= NtUserCreateWindowEx( 0, &class_name
, &class_name
, &name
,
288 WS_POPUP
| WS_DISABLED
| WS_CLIPSIBLINGS
,
289 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, FALSE
);
295 void unregister_imm_window( HWND hwnd
)
297 struct imm_thread_data
*thread_data
= get_user_thread_info()->imm_thread_data
;
299 if (!thread_data
) return;
300 if (thread_data
->default_hwnd
== hwnd
)
302 detach_default_window( thread_data
);
306 if (!(win_set_flags( hwnd
, 0, WIN_HAS_IME_WIN
) & WIN_HAS_IME_WIN
)) return;
308 /* destroy default IME window */
309 TRACE( "unregister IME window for %p\n", hwnd
);
310 if (!--thread_data
->window_cnt
)
312 HWND destroy_hwnd
= detach_default_window( thread_data
);
313 if (destroy_hwnd
) NtUserDestroyWindow( destroy_hwnd
);
317 /***********************************************************************
318 * NtUserDisableThreadIme (win32u.@)
320 BOOL WINAPI
NtUserDisableThreadIme( DWORD thread_id
)
322 struct imm_thread_data
*thread_data
;
328 pthread_mutex_lock( &imm_mutex
);
329 LIST_FOR_EACH_ENTRY( thread_data
, &thread_data_list
, struct imm_thread_data
, entry
)
331 if (thread_data
->thread_id
== GetCurrentThreadId()) continue;
332 if (!thread_data
->default_hwnd
) continue;
333 NtUserMessageCall( thread_data
->default_hwnd
, WM_WINE_DESTROYWINDOW
, 0, 0,
334 0, NtUserSendNotifyMessage
, FALSE
);
336 pthread_mutex_unlock( &imm_mutex
);
338 else if (!thread_id
|| thread_id
== GetCurrentThreadId())
340 if (!(thread_data
= get_imm_thread_data())) return FALSE
;
341 thread_data
->disable_ime
= TRUE
;
345 if ((thread_data
= get_user_thread_info()->imm_thread_data
))
347 HWND destroy_hwnd
= detach_default_window( thread_data
);
348 NtUserDestroyWindow( destroy_hwnd
);
353 HWND
get_default_ime_window( HWND hwnd
)
355 struct imm_thread_data
*thread_data
;
362 if (!(thread_id
= get_window_thread( hwnd
, NULL
))) return 0;
364 pthread_mutex_lock( &imm_mutex
);
365 LIST_FOR_EACH_ENTRY( thread_data
, &thread_data_list
, struct imm_thread_data
, entry
)
367 if (thread_data
->thread_id
!= thread_id
) continue;
368 ret
= thread_data
->default_hwnd
;
371 pthread_mutex_unlock( &imm_mutex
);
373 else if ((thread_data
= get_user_thread_info()->imm_thread_data
))
375 ret
= thread_data
->default_hwnd
;
378 TRACE( "default for %p is %p\n", hwnd
, ret
);
382 void cleanup_imm_thread(void)
384 struct user_thread_info
*thread_info
= get_user_thread_info();
386 if (thread_info
->imm_thread_data
)
388 pthread_mutex_lock( &imm_mutex
);
389 list_remove( &thread_info
->imm_thread_data
->entry
);
390 pthread_mutex_unlock( &imm_mutex
);
391 free( thread_info
->imm_thread_data
);
392 thread_info
->imm_thread_data
= NULL
;
395 NtUserDestroyInputContext( UlongToHandle( thread_info
->client_info
.default_imc
));
398 /*****************************************************************************
399 * NtUserBuildHimcList (win32u.@)
401 NTSTATUS WINAPI
NtUserBuildHimcList( UINT thread_id
, UINT count
, HIMC
*buffer
, UINT
*size
)
406 TRACE( "thread_id %#x, count %u, buffer %p, size %p\n", thread_id
, count
, buffer
, size
);
408 if (!buffer
) return STATUS_UNSUCCESSFUL
;
409 if (!thread_id
) thread_id
= GetCurrentThreadId();
413 while (count
&& (imc
= next_process_user_handle_ptr( &handle
, NTUSER_OBJ_IMC
)))
415 if (thread_id
!= -1 && imc
->thread_id
!= thread_id
) continue;
416 buffer
[(*size
)++] = handle
;
421 return STATUS_SUCCESS
;
424 BOOL WINAPI DECLSPEC_HIDDEN
ImmProcessKey( HWND hwnd
, HKL hkl
, UINT vkey
, LPARAM key_data
, DWORD unknown
)
426 struct imm_process_key_params params
=
427 { .hwnd
= hwnd
, .hkl
= hkl
, .vkey
= vkey
, .key_data
= key_data
};
430 return KeUserModeCallback( NtUserImmProcessKey
, ¶ms
, sizeof(params
), &ret_ptr
, &ret_len
);
433 BOOL WINAPI DECLSPEC_HIDDEN
ImmTranslateMessage( HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM key_data
)
435 struct imm_translate_message_params params
=
436 { .hwnd
= hwnd
, .msg
= msg
, .wparam
= wparam
, .key_data
= key_data
};
439 return KeUserModeCallback( NtUserImmTranslateMessage
, ¶ms
, sizeof(params
),
440 &ret_ptr
, &ret_len
);