mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-31 12:19:49 +00:00
638 lines
19 KiB
C
638 lines
19 KiB
C
/*
|
|
* Input Context implementation
|
|
*
|
|
* Copyright 1998 Patrik Stridvall
|
|
* Copyright 2002, 2003, 2007 CodeWeavers, Aric Stewart
|
|
* Copyright 2022 Jacek Caban for CodeWeavers
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#if 0
|
|
#pragma makedep unix
|
|
#endif
|
|
|
|
#include <pthread.h>
|
|
#include "ntstatus.h"
|
|
#define WIN32_NO_STATUS
|
|
#include "win32u_private.h"
|
|
#include "ntuser_private.h"
|
|
#include "immdev.h"
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(imm);
|
|
|
|
struct ime_update
|
|
{
|
|
struct list entry;
|
|
WORD vkey;
|
|
WORD scan;
|
|
DWORD cursor_pos;
|
|
WCHAR *comp_str;
|
|
WCHAR *result_str;
|
|
WCHAR buffer[];
|
|
};
|
|
|
|
struct imc
|
|
{
|
|
struct user_object obj;
|
|
DWORD thread_id;
|
|
UINT_PTR client_ptr;
|
|
};
|
|
|
|
struct imm_thread_data
|
|
{
|
|
struct list entry;
|
|
DWORD thread_id;
|
|
HWND default_hwnd;
|
|
BOOL disable_ime;
|
|
UINT window_cnt;
|
|
WORD ime_process_scan; /* scan code of the key being processed */
|
|
WORD ime_process_vkey; /* vkey of the key being processed */
|
|
struct ime_update *update; /* result of ImeProcessKey */
|
|
};
|
|
|
|
static struct list thread_data_list = LIST_INIT( thread_data_list );
|
|
static pthread_mutex_t imm_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
static struct list ime_updates = LIST_INIT( ime_updates );
|
|
static BOOL disable_ime;
|
|
|
|
static struct imc *get_imc_ptr( HIMC handle )
|
|
{
|
|
struct imc *imc = get_user_handle_ptr( handle, NTUSER_OBJ_IMC );
|
|
if (imc && imc != OBJ_OTHER_PROCESS) return imc;
|
|
WARN( "invalid handle %p\n", handle );
|
|
RtlSetLastWin32Error( ERROR_INVALID_HANDLE );
|
|
return NULL;
|
|
}
|
|
|
|
static void release_imc_ptr( struct imc *imc )
|
|
{
|
|
release_user_handle_ptr( imc );
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtUserCreateInputContext (win32u.@)
|
|
*/
|
|
HIMC WINAPI NtUserCreateInputContext( UINT_PTR client_ptr )
|
|
{
|
|
struct imc *imc;
|
|
HIMC handle;
|
|
|
|
if (!(imc = malloc( sizeof(*imc) ))) return 0;
|
|
imc->client_ptr = client_ptr;
|
|
imc->thread_id = GetCurrentThreadId();
|
|
if (!(handle = alloc_user_handle( &imc->obj, NTUSER_OBJ_IMC )))
|
|
{
|
|
free( imc );
|
|
return 0;
|
|
}
|
|
|
|
TRACE( "%lx returning %p\n", (long)client_ptr, handle );
|
|
return handle;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtUserDestroyInputContext (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtUserDestroyInputContext( HIMC handle )
|
|
{
|
|
struct imc *imc;
|
|
|
|
TRACE( "%p\n", handle );
|
|
|
|
if (!(imc = free_user_handle( handle, NTUSER_OBJ_IMC ))) return FALSE;
|
|
if (imc == OBJ_OTHER_PROCESS)
|
|
{
|
|
FIXME( "other process handle %p\n", handle );
|
|
return FALSE;
|
|
}
|
|
free( imc );
|
|
return TRUE;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtUserUpdateInputContext (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtUserUpdateInputContext( HIMC handle, UINT attr, UINT_PTR value )
|
|
{
|
|
struct imc *imc;
|
|
BOOL ret = TRUE;
|
|
|
|
TRACE( "%p %u %lx\n", handle, attr, (long)value );
|
|
|
|
if (!(imc = get_imc_ptr( handle ))) return FALSE;
|
|
|
|
switch (attr)
|
|
{
|
|
case NtUserInputContextClientPtr:
|
|
imc->client_ptr = value;
|
|
break;
|
|
|
|
default:
|
|
FIXME( "unknown attr %u\n", attr );
|
|
ret = FALSE;
|
|
};
|
|
|
|
release_imc_ptr( imc );
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtUserQueryInputContext (win32u.@)
|
|
*/
|
|
UINT_PTR WINAPI NtUserQueryInputContext( HIMC handle, UINT attr )
|
|
{
|
|
struct imc *imc;
|
|
UINT_PTR ret;
|
|
|
|
if (!(imc = get_imc_ptr( handle ))) return FALSE;
|
|
|
|
switch (attr)
|
|
{
|
|
case NtUserInputContextClientPtr:
|
|
ret = imc->client_ptr;
|
|
break;
|
|
|
|
case NtUserInputContextThreadId:
|
|
ret = imc->thread_id;
|
|
break;
|
|
|
|
default:
|
|
FIXME( "unknown attr %u\n", attr );
|
|
ret = 0;
|
|
};
|
|
|
|
release_imc_ptr( imc );
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtUserAssociateInputContext (win32u.@)
|
|
*/
|
|
UINT WINAPI NtUserAssociateInputContext( HWND hwnd, HIMC ctx, ULONG flags )
|
|
{
|
|
WND *win;
|
|
UINT ret = AICR_OK;
|
|
|
|
TRACE( "%p %p %x\n", hwnd, ctx, (int)flags );
|
|
|
|
switch (flags)
|
|
{
|
|
case 0:
|
|
case IACE_IGNORENOCONTEXT:
|
|
case IACE_DEFAULT:
|
|
break;
|
|
|
|
default:
|
|
FIXME( "unknown flags 0x%x\n", (int)flags );
|
|
return AICR_FAILED;
|
|
}
|
|
|
|
if (flags == IACE_DEFAULT)
|
|
{
|
|
if (!(ctx = get_default_input_context())) return AICR_FAILED;
|
|
}
|
|
else if (ctx)
|
|
{
|
|
if (NtUserQueryInputContext( ctx, NtUserInputContextThreadId ) != GetCurrentThreadId())
|
|
return AICR_FAILED;
|
|
}
|
|
|
|
if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS || win == WND_DESKTOP)
|
|
return AICR_FAILED;
|
|
|
|
if (ctx && win->tid != GetCurrentThreadId()) ret = AICR_FAILED;
|
|
else if (flags != IACE_IGNORENOCONTEXT || win->imc)
|
|
{
|
|
if (win->imc != ctx && get_focus() == hwnd) ret = AICR_FOCUS_CHANGED;
|
|
win->imc = ctx;
|
|
}
|
|
|
|
release_win_ptr( win );
|
|
return ret;
|
|
}
|
|
|
|
HIMC get_default_input_context(void)
|
|
{
|
|
struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
|
|
if (!thread_info->default_imc)
|
|
thread_info->default_imc = HandleToUlong( NtUserCreateInputContext( 0 ));
|
|
return UlongToHandle( thread_info->default_imc );
|
|
}
|
|
|
|
HIMC get_window_input_context( HWND hwnd )
|
|
{
|
|
WND *win;
|
|
HIMC ret;
|
|
|
|
if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS || win == WND_DESKTOP)
|
|
{
|
|
RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
|
|
return 0;
|
|
}
|
|
|
|
ret = win->imc;
|
|
release_win_ptr( win );
|
|
return ret;
|
|
}
|
|
|
|
static HWND detach_default_window( struct imm_thread_data *thread_data )
|
|
{
|
|
HWND hwnd = thread_data->default_hwnd;
|
|
thread_data->default_hwnd = NULL;
|
|
thread_data->window_cnt = 0;
|
|
return hwnd;
|
|
}
|
|
|
|
static struct imm_thread_data *get_imm_thread_data(void)
|
|
{
|
|
struct user_thread_info *thread_info = get_user_thread_info();
|
|
if (!thread_info->imm_thread_data)
|
|
{
|
|
struct imm_thread_data *data;
|
|
if (!(data = calloc( 1, sizeof( *data )))) return NULL;
|
|
data->thread_id = GetCurrentThreadId();
|
|
|
|
pthread_mutex_lock( &imm_mutex );
|
|
list_add_tail( &thread_data_list, &data->entry );
|
|
pthread_mutex_unlock( &imm_mutex );
|
|
|
|
thread_info->imm_thread_data = data;
|
|
}
|
|
return thread_info->imm_thread_data;
|
|
}
|
|
|
|
BOOL register_imm_window( HWND hwnd )
|
|
{
|
|
struct imm_thread_data *thread_data;
|
|
|
|
TRACE( "(%p)\n", hwnd );
|
|
|
|
if (disable_ime || !needs_ime_window( hwnd ))
|
|
return FALSE;
|
|
|
|
thread_data = get_imm_thread_data();
|
|
if (!thread_data || thread_data->disable_ime)
|
|
return FALSE;
|
|
|
|
TRACE( "window_cnt=%u, default_hwnd=%p\n", thread_data->window_cnt + 1, thread_data->default_hwnd );
|
|
|
|
/* Create default IME window */
|
|
if (!thread_data->window_cnt++)
|
|
{
|
|
static const WCHAR imeW[] = {'I','M','E',0};
|
|
static const WCHAR default_imeW[] = {'D','e','f','a','u','l','t',' ','I','M','E',0};
|
|
UNICODE_STRING class_name = RTL_CONSTANT_STRING( imeW );
|
|
UNICODE_STRING name = RTL_CONSTANT_STRING( default_imeW );
|
|
|
|
thread_data->default_hwnd = NtUserCreateWindowEx( 0, &class_name, &class_name, &name,
|
|
WS_POPUP | WS_DISABLED | WS_CLIPSIBLINGS,
|
|
0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, FALSE );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void unregister_imm_window( HWND hwnd )
|
|
{
|
|
struct imm_thread_data *thread_data = get_user_thread_info()->imm_thread_data;
|
|
|
|
if (!thread_data) return;
|
|
if (thread_data->default_hwnd == hwnd)
|
|
{
|
|
detach_default_window( thread_data );
|
|
return;
|
|
}
|
|
|
|
if (!(win_set_flags( hwnd, 0, WIN_HAS_IME_WIN ) & WIN_HAS_IME_WIN)) return;
|
|
|
|
/* destroy default IME window */
|
|
TRACE( "unregister IME window for %p\n", hwnd );
|
|
if (!--thread_data->window_cnt)
|
|
{
|
|
HWND destroy_hwnd = detach_default_window( thread_data );
|
|
if (destroy_hwnd) NtUserDestroyWindow( destroy_hwnd );
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtUserDisableThreadIme (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtUserDisableThreadIme( DWORD thread_id )
|
|
{
|
|
struct imm_thread_data *thread_data;
|
|
|
|
if (thread_id == -1)
|
|
{
|
|
disable_ime = TRUE;
|
|
|
|
pthread_mutex_lock( &imm_mutex );
|
|
LIST_FOR_EACH_ENTRY( thread_data, &thread_data_list, struct imm_thread_data, entry )
|
|
{
|
|
if (thread_data->thread_id == GetCurrentThreadId()) continue;
|
|
if (!thread_data->default_hwnd) continue;
|
|
NtUserMessageCall( thread_data->default_hwnd, WM_WINE_DESTROYWINDOW, 0, 0,
|
|
0, NtUserSendNotifyMessage, FALSE );
|
|
}
|
|
pthread_mutex_unlock( &imm_mutex );
|
|
}
|
|
else if (!thread_id || thread_id == GetCurrentThreadId())
|
|
{
|
|
if (!(thread_data = get_imm_thread_data())) return FALSE;
|
|
thread_data->disable_ime = TRUE;
|
|
}
|
|
else return FALSE;
|
|
|
|
if ((thread_data = get_user_thread_info()->imm_thread_data))
|
|
{
|
|
HWND destroy_hwnd = detach_default_window( thread_data );
|
|
NtUserDestroyWindow( destroy_hwnd );
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
HWND get_default_ime_window( HWND hwnd )
|
|
{
|
|
struct imm_thread_data *thread_data;
|
|
HWND ret = 0;
|
|
|
|
if (hwnd)
|
|
{
|
|
DWORD thread_id;
|
|
|
|
if (!(thread_id = get_window_thread( hwnd, NULL ))) return 0;
|
|
|
|
pthread_mutex_lock( &imm_mutex );
|
|
LIST_FOR_EACH_ENTRY( thread_data, &thread_data_list, struct imm_thread_data, entry )
|
|
{
|
|
if (thread_data->thread_id != thread_id) continue;
|
|
ret = thread_data->default_hwnd;
|
|
break;
|
|
}
|
|
pthread_mutex_unlock( &imm_mutex );
|
|
}
|
|
else if ((thread_data = get_user_thread_info()->imm_thread_data))
|
|
{
|
|
ret = thread_data->default_hwnd;
|
|
}
|
|
|
|
TRACE( "default for %p is %p\n", hwnd, ret );
|
|
return ret;
|
|
}
|
|
|
|
void cleanup_imm_thread(void)
|
|
{
|
|
struct user_thread_info *thread_info = get_user_thread_info();
|
|
|
|
if (thread_info->imm_thread_data)
|
|
{
|
|
pthread_mutex_lock( &imm_mutex );
|
|
list_remove( &thread_info->imm_thread_data->entry );
|
|
pthread_mutex_unlock( &imm_mutex );
|
|
free( thread_info->imm_thread_data );
|
|
thread_info->imm_thread_data = NULL;
|
|
}
|
|
|
|
NtUserDestroyInputContext( UlongToHandle( thread_info->client_info.default_imc ));
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* NtUserBuildHimcList (win32u.@)
|
|
*/
|
|
NTSTATUS WINAPI NtUserBuildHimcList( UINT thread_id, UINT count, HIMC *buffer, UINT *size )
|
|
{
|
|
HANDLE handle = 0;
|
|
struct imc *imc;
|
|
|
|
TRACE( "thread_id %#x, count %u, buffer %p, size %p\n", thread_id, count, buffer, size );
|
|
|
|
if (!buffer) return STATUS_UNSUCCESSFUL;
|
|
if (!thread_id) thread_id = GetCurrentThreadId();
|
|
|
|
*size = 0;
|
|
user_lock();
|
|
while (count && (imc = next_process_user_handle_ptr( &handle, NTUSER_OBJ_IMC )))
|
|
{
|
|
if (thread_id != -1 && imc->thread_id != thread_id) continue;
|
|
buffer[(*size)++] = handle;
|
|
count--;
|
|
}
|
|
user_unlock();
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static void post_ime_update( HWND hwnd, UINT cursor_pos, WCHAR *comp_str, WCHAR *result_str )
|
|
{
|
|
static UINT ime_update_count;
|
|
|
|
struct imm_thread_data *data = get_imm_thread_data();
|
|
UINT id = -1, comp_len, result_len;
|
|
struct ime_update *update;
|
|
|
|
TRACE( "hwnd %p, cursor_pos %u, comp_str %s, result_str %s\n", hwnd, cursor_pos,
|
|
debugstr_w(comp_str), debugstr_w(result_str) );
|
|
|
|
comp_len = comp_str ? wcslen( comp_str ) + 1 : 0;
|
|
result_len = result_str ? wcslen( result_str ) + 1 : 0;
|
|
|
|
if (!(update = malloc( offsetof(struct ime_update, buffer[comp_len + result_len]) ))) return;
|
|
update->cursor_pos = cursor_pos;
|
|
update->comp_str = comp_str ? memcpy( update->buffer, comp_str, comp_len * sizeof(WCHAR) ) : NULL;
|
|
update->result_str = result_str ? memcpy( update->buffer + comp_len, result_str, result_len * sizeof(WCHAR) ) : NULL;
|
|
|
|
if (!(update->vkey = data->ime_process_vkey))
|
|
{
|
|
pthread_mutex_lock( &imm_mutex );
|
|
id = update->scan = ++ime_update_count;
|
|
update->vkey = VK_PROCESSKEY;
|
|
list_add_tail( &ime_updates, &update->entry );
|
|
pthread_mutex_unlock( &imm_mutex );
|
|
|
|
NtUserPostMessage( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_COMP_STRING, id );
|
|
}
|
|
else
|
|
{
|
|
update->scan = data->ime_process_scan;
|
|
free( data->update );
|
|
data->update = update;
|
|
}
|
|
}
|
|
|
|
static struct ime_update *find_ime_update( WORD vkey, WORD scan )
|
|
{
|
|
struct ime_update *update;
|
|
|
|
LIST_FOR_EACH_ENTRY( update, &ime_updates, struct ime_update, entry )
|
|
if (update->vkey == vkey && update->scan == scan) return update;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
UINT ime_to_tascii_ex( UINT vkey, UINT lparam, const BYTE *state, COMPOSITIONSTRING *compstr, HIMC himc )
|
|
{
|
|
UINT needed = sizeof(COMPOSITIONSTRING), comp_len, result_len;
|
|
struct ime_update *update;
|
|
void *dst;
|
|
|
|
TRACE( "vkey %#x, lparam %#x, state %p, compstr %p, himc %p\n", vkey, lparam, state, compstr, himc );
|
|
|
|
pthread_mutex_lock( &imm_mutex );
|
|
|
|
if (!(update = find_ime_update( vkey, lparam )))
|
|
{
|
|
pthread_mutex_unlock( &imm_mutex );
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
if (!update->comp_str) comp_len = 0;
|
|
else
|
|
{
|
|
comp_len = wcslen( update->comp_str );
|
|
needed += comp_len * sizeof(WCHAR); /* GCS_COMPSTR */
|
|
needed += comp_len; /* GCS_COMPATTR */
|
|
needed += 2 * sizeof(DWORD); /* GCS_COMPCLAUSE */
|
|
}
|
|
|
|
if (!update->result_str) result_len = 0;
|
|
else
|
|
{
|
|
result_len = wcslen( update->result_str );
|
|
needed += result_len * sizeof(WCHAR); /* GCS_RESULTSTR */
|
|
needed += 2 * sizeof(DWORD); /* GCS_RESULTCLAUSE */
|
|
}
|
|
|
|
if (compstr->dwSize < needed)
|
|
{
|
|
compstr->dwSize = needed;
|
|
pthread_mutex_unlock( &imm_mutex );
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
list_remove( &update->entry );
|
|
pthread_mutex_unlock( &imm_mutex );
|
|
|
|
memset( compstr, 0, sizeof(*compstr) );
|
|
compstr->dwSize = sizeof(*compstr);
|
|
|
|
if (update->comp_str)
|
|
{
|
|
compstr->dwCursorPos = update->cursor_pos;
|
|
|
|
compstr->dwCompStrLen = comp_len;
|
|
compstr->dwCompStrOffset = compstr->dwSize;
|
|
dst = (BYTE *)compstr + compstr->dwCompStrOffset;
|
|
memcpy( dst, update->comp_str, compstr->dwCompStrLen * sizeof(WCHAR) );
|
|
compstr->dwSize += compstr->dwCompStrLen * sizeof(WCHAR);
|
|
|
|
compstr->dwCompClauseLen = 2 * sizeof(DWORD);
|
|
compstr->dwCompClauseOffset = compstr->dwSize;
|
|
dst = (BYTE *)compstr + compstr->dwCompClauseOffset;
|
|
*((DWORD *)dst + 0) = 0;
|
|
*((DWORD *)dst + 1) = compstr->dwCompStrLen;
|
|
compstr->dwSize += compstr->dwCompClauseLen;
|
|
|
|
compstr->dwCompAttrLen = compstr->dwCompStrLen;
|
|
compstr->dwCompAttrOffset = compstr->dwSize;
|
|
dst = (BYTE *)compstr + compstr->dwCompAttrOffset;
|
|
memset( dst, ATTR_INPUT, compstr->dwCompAttrLen );
|
|
compstr->dwSize += compstr->dwCompAttrLen;
|
|
}
|
|
|
|
if (update->result_str)
|
|
{
|
|
compstr->dwResultStrLen = result_len;
|
|
compstr->dwResultStrOffset = compstr->dwSize;
|
|
dst = (BYTE *)compstr + compstr->dwResultStrOffset;
|
|
memcpy( dst, update->result_str, compstr->dwResultStrLen * sizeof(WCHAR) );
|
|
compstr->dwSize += compstr->dwResultStrLen * sizeof(WCHAR);
|
|
|
|
compstr->dwResultClauseLen = 2 * sizeof(DWORD);
|
|
compstr->dwResultClauseOffset = compstr->dwSize;
|
|
dst = (BYTE *)compstr + compstr->dwResultClauseOffset;
|
|
*((DWORD *)dst + 0) = 0;
|
|
*((DWORD *)dst + 1) = compstr->dwResultStrLen;
|
|
compstr->dwSize += compstr->dwResultClauseLen;
|
|
}
|
|
|
|
free( update );
|
|
return 0;
|
|
}
|
|
|
|
LRESULT ime_driver_call( HWND hwnd, enum wine_ime_call call, WPARAM wparam, LPARAM lparam,
|
|
struct ime_driver_call_params *params )
|
|
{
|
|
LRESULT res;
|
|
|
|
switch (call)
|
|
{
|
|
case WINE_IME_PROCESS_KEY:
|
|
{
|
|
struct imm_thread_data *data = get_imm_thread_data();
|
|
|
|
data->ime_process_scan = HIWORD(lparam) & 0x1ff;
|
|
data->ime_process_vkey = LOWORD(wparam);
|
|
res = user_driver->pImeProcessKey( params->himc, wparam, lparam, params->state );
|
|
data->ime_process_vkey = data->ime_process_scan = 0;
|
|
|
|
if (data->update)
|
|
{
|
|
pthread_mutex_lock( &imm_mutex );
|
|
list_add_tail( &ime_updates, &data->update->entry );
|
|
pthread_mutex_unlock( &imm_mutex );
|
|
data->update = NULL;
|
|
res = TRUE;
|
|
}
|
|
|
|
TRACE( "processing scan %#x, vkey %#x -> %u\n", LOWORD(wparam), HIWORD(lparam) & 0x1ff, (UINT)res );
|
|
return res;
|
|
}
|
|
case WINE_IME_TO_ASCII_EX:
|
|
return ime_to_tascii_ex( wparam, lparam, params->state, params->compstr, params->himc );
|
|
case WINE_IME_POST_UPDATE:
|
|
post_ime_update( hwnd, wparam, (WCHAR *)lparam, (WCHAR *)params );
|
|
return 0;
|
|
default:
|
|
ERR( "Unknown IME driver call %#x\n", call );
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* NtUserNotifyIMEStatus (win32u.@)
|
|
*/
|
|
void WINAPI NtUserNotifyIMEStatus( HWND hwnd, UINT status )
|
|
{
|
|
user_driver->pNotifyIMEStatus( hwnd, status );
|
|
}
|
|
|
|
BOOL WINAPI ImmProcessKey( HWND hwnd, HKL hkl, UINT vkey, LPARAM key_data, DWORD unknown )
|
|
{
|
|
struct imm_process_key_params params =
|
|
{ .hwnd = hwnd, .hkl = hkl, .vkey = vkey, .key_data = key_data };
|
|
void *ret_ptr;
|
|
ULONG ret_len;
|
|
return !KeUserModeCallback( NtUserImmProcessKey, ¶ms, sizeof(params), &ret_ptr, &ret_len );
|
|
}
|
|
|
|
BOOL WINAPI ImmTranslateMessage( HWND hwnd, UINT msg, WPARAM wparam, LPARAM key_data )
|
|
{
|
|
struct imm_translate_message_params params =
|
|
{ .hwnd = hwnd, .msg = msg, .wparam = wparam, .key_data = key_data };
|
|
void *ret_ptr;
|
|
ULONG ret_len;
|
|
return !KeUserModeCallback( NtUserImmTranslateMessage, ¶ms, sizeof(params), &ret_ptr, &ret_len );
|
|
}
|