mirror of
git://source.winehq.org/git/wine.git
synced 2024-09-02 15:33:38 +00:00
![Rémi Bernon](/assets/img/avatar_default.png)
The composition or result strings may be present, but with zero length. In which case we still want to send the messages, to indicate any change for instance whenever a character is deleted and strings become empty. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55055
762 lines
25 KiB
C
762 lines
25 KiB
C
/*
|
|
* Copyright 2023 Rémi Bernon 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
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
|
|
#include "imm_private.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(imm);
|
|
|
|
struct ime_private
|
|
{
|
|
BOOL in_composition;
|
|
HFONT hfont;
|
|
};
|
|
|
|
static const char *debugstr_imn( WPARAM wparam )
|
|
{
|
|
switch (wparam)
|
|
{
|
|
case IMN_OPENSTATUSWINDOW: return "IMN_OPENSTATUSWINDOW";
|
|
case IMN_CLOSESTATUSWINDOW: return "IMN_CLOSESTATUSWINDOW";
|
|
case IMN_OPENCANDIDATE: return "IMN_OPENCANDIDATE";
|
|
case IMN_CHANGECANDIDATE: return "IMN_CHANGECANDIDATE";
|
|
case IMN_CLOSECANDIDATE: return "IMN_CLOSECANDIDATE";
|
|
case IMN_SETCONVERSIONMODE: return "IMN_SETCONVERSIONMODE";
|
|
case IMN_SETSENTENCEMODE: return "IMN_SETSENTENCEMODE";
|
|
case IMN_SETOPENSTATUS: return "IMN_SETOPENSTATUS";
|
|
case IMN_SETCANDIDATEPOS: return "IMN_SETCANDIDATEPOS";
|
|
case IMN_SETCOMPOSITIONFONT: return "IMN_SETCOMPOSITIONFONT";
|
|
case IMN_SETCOMPOSITIONWINDOW: return "IMN_SETCOMPOSITIONWINDOW";
|
|
case IMN_GUIDELINE: return "IMN_GUIDELINE";
|
|
case IMN_SETSTATUSWINDOWPOS: return "IMN_SETSTATUSWINDOWPOS";
|
|
case IMN_WINE_SET_OPEN_STATUS: return "IMN_WINE_SET_OPEN_STATUS";
|
|
case IMN_WINE_SET_COMP_STRING: return "IMN_WINE_SET_COMP_STRING";
|
|
default: return wine_dbg_sprintf( "%#Ix", wparam );
|
|
}
|
|
}
|
|
|
|
static const char *debugstr_imc( WPARAM wparam )
|
|
{
|
|
switch (wparam)
|
|
{
|
|
case IMC_GETCANDIDATEPOS: return "IMC_GETCANDIDATEPOS";
|
|
case IMC_SETCANDIDATEPOS: return "IMC_SETCANDIDATEPOS";
|
|
case IMC_GETCOMPOSITIONFONT: return "IMC_GETCOMPOSITIONFONT";
|
|
case IMC_SETCOMPOSITIONFONT: return "IMC_SETCOMPOSITIONFONT";
|
|
case IMC_GETCOMPOSITIONWINDOW: return "IMC_GETCOMPOSITIONWINDOW";
|
|
case IMC_SETCOMPOSITIONWINDOW: return "IMC_SETCOMPOSITIONWINDOW";
|
|
case IMC_GETSTATUSWINDOWPOS: return "IMC_GETSTATUSWINDOWPOS";
|
|
case IMC_SETSTATUSWINDOWPOS: return "IMC_SETSTATUSWINDOWPOS";
|
|
case IMC_CLOSESTATUSWINDOW: return "IMC_CLOSESTATUSWINDOW";
|
|
case IMC_OPENSTATUSWINDOW: return "IMC_OPENSTATUSWINDOW";
|
|
default: return wine_dbg_sprintf( "%#Ix", wparam );
|
|
}
|
|
}
|
|
|
|
static WCHAR *input_context_get_comp_str( INPUTCONTEXT *ctx, BOOL result, UINT *length )
|
|
{
|
|
COMPOSITIONSTRING *string;
|
|
WCHAR *text = NULL;
|
|
UINT len, off;
|
|
|
|
if (!(string = ImmLockIMCC( ctx->hCompStr ))) return NULL;
|
|
len = result ? string->dwResultStrLen : string->dwCompStrLen;
|
|
off = result ? string->dwResultStrOffset : string->dwCompStrOffset;
|
|
|
|
if (len && off && (text = malloc( (len + 1) * sizeof(WCHAR) )))
|
|
{
|
|
memcpy( text, (BYTE *)string + off, len * sizeof(WCHAR) );
|
|
text[len] = 0;
|
|
*length = len;
|
|
}
|
|
|
|
ImmUnlockIMCC( ctx->hCompStr );
|
|
return text;
|
|
}
|
|
|
|
static void input_context_set_comp_str( INPUTCONTEXT *ctx, const WCHAR *str, UINT len )
|
|
{
|
|
COMPOSITIONSTRING *compstr;
|
|
HIMCC himcc;
|
|
UINT size;
|
|
BYTE *dst;
|
|
|
|
size = sizeof(*compstr);
|
|
size += len * sizeof(WCHAR); /* GCS_COMPSTR */
|
|
size += len; /* GCS_COMPSTRATTR */
|
|
size += 2 * sizeof(DWORD); /* GCS_COMPSTRCLAUSE */
|
|
|
|
if (!(himcc = ImmReSizeIMCC( ctx->hCompStr, size )))
|
|
WARN( "Failed to resize input context composition string\n" );
|
|
else if (!(compstr = ImmLockIMCC( (ctx->hCompStr = himcc) )))
|
|
WARN( "Failed to lock input context composition string\n" );
|
|
else
|
|
{
|
|
memset( compstr, 0, sizeof(*compstr) );
|
|
compstr->dwSize = sizeof(*compstr);
|
|
|
|
if (len)
|
|
{
|
|
compstr->dwCursorPos = len;
|
|
|
|
compstr->dwCompStrLen = len;
|
|
compstr->dwCompStrOffset = compstr->dwSize;
|
|
dst = (BYTE *)compstr + compstr->dwCompStrOffset;
|
|
memcpy( dst, 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;
|
|
}
|
|
|
|
ImmUnlockIMCC( ctx->hCompStr );
|
|
}
|
|
}
|
|
|
|
static HFONT input_context_select_ui_font( INPUTCONTEXT *ctx, HDC hdc )
|
|
{
|
|
struct ime_private *priv;
|
|
HFONT font = NULL;
|
|
if (!(priv = ImmLockIMCC( ctx->hPrivate ))) return NULL;
|
|
if (priv->hfont) font = SelectObject( hdc, priv->hfont );
|
|
ImmUnlockIMCC( ctx->hPrivate );
|
|
return font;
|
|
}
|
|
|
|
static void ime_send_message( HIMC himc, UINT message, WPARAM wparam, LPARAM lparam )
|
|
{
|
|
INPUTCONTEXT *ctx;
|
|
TRANSMSG *msgs;
|
|
HIMCC himcc;
|
|
|
|
if (!(ctx = ImmLockIMC( himc ))) return;
|
|
if (!(himcc = ImmReSizeIMCC( ctx->hMsgBuf, (ctx->dwNumMsgBuf + 1) * sizeof(*msgs) )))
|
|
WARN( "Failed to resize input context message buffer\n" );
|
|
else if (!(msgs = ImmLockIMCC( (ctx->hMsgBuf = himcc) )))
|
|
WARN( "Failed to lock input context message buffer\n" );
|
|
else
|
|
{
|
|
TRANSMSG msg = {.message = message, .wParam = wparam, .lParam = lparam};
|
|
msgs[ctx->dwNumMsgBuf++] = msg;
|
|
ImmUnlockIMCC( ctx->hMsgBuf );
|
|
}
|
|
|
|
ImmUnlockIMC( himc );
|
|
ImmGenerateMessage( himc );
|
|
}
|
|
|
|
static UINT ime_set_composition_status( HIMC himc, BOOL composition )
|
|
{
|
|
struct ime_private *priv;
|
|
INPUTCONTEXT *ctx;
|
|
UINT msg = 0;
|
|
|
|
if (!(ctx = ImmLockIMC( himc ))) return 0;
|
|
if ((priv = ImmLockIMCC( ctx->hPrivate )))
|
|
{
|
|
if (!priv->in_composition && composition) msg = WM_IME_STARTCOMPOSITION;
|
|
else if (priv->in_composition && !composition) msg = WM_IME_ENDCOMPOSITION;
|
|
priv->in_composition = composition;
|
|
ImmUnlockIMCC( ctx->hPrivate );
|
|
}
|
|
ImmUnlockIMC( himc );
|
|
|
|
return msg;
|
|
}
|
|
|
|
static void ime_ui_paint( HIMC himc, HWND hwnd )
|
|
{
|
|
PAINTSTRUCT ps;
|
|
RECT rect, new_rect;
|
|
HDC hdc;
|
|
HMONITOR monitor;
|
|
MONITORINFO mon_info;
|
|
INPUTCONTEXT *ctx;
|
|
POINT offset;
|
|
WCHAR *str;
|
|
UINT len;
|
|
|
|
if (!(ctx = ImmLockIMC( himc ))) return;
|
|
|
|
hdc = BeginPaint( hwnd, &ps );
|
|
|
|
GetClientRect( hwnd, &rect );
|
|
FillRect( hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1) );
|
|
new_rect = rect;
|
|
|
|
if ((str = input_context_get_comp_str( ctx, FALSE, &len )))
|
|
{
|
|
HFONT font = input_context_select_ui_font( ctx, hdc );
|
|
SIZE size;
|
|
POINT pt;
|
|
|
|
GetTextExtentPoint32W( hdc, str, len, &size );
|
|
pt.x = size.cx;
|
|
pt.y = size.cy;
|
|
LPtoDP( hdc, &pt, 1 );
|
|
|
|
/*
|
|
* How this works based on tests on windows:
|
|
* CFS_POINT: then we start our window at the point and grow it as large
|
|
* as it needs to be for the string.
|
|
* CFS_RECT: we still use the ptCurrentPos as a starting point and our
|
|
* window is only as large as we need for the string, but we do not
|
|
* grow such that our window exceeds the given rect. Wrapping if
|
|
* needed and possible. If our ptCurrentPos is outside of our rect
|
|
* then no window is displayed.
|
|
* CFS_FORCE_POSITION: appears to behave just like CFS_POINT
|
|
* maybe because the default MSIME does not do any IME adjusting.
|
|
*/
|
|
if (ctx->cfCompForm.dwStyle != CFS_DEFAULT)
|
|
{
|
|
POINT cpt = ctx->cfCompForm.ptCurrentPos;
|
|
ClientToScreen( ctx->hWnd, &cpt );
|
|
rect.left = cpt.x;
|
|
rect.top = cpt.y;
|
|
rect.right = rect.left + pt.x;
|
|
rect.bottom = rect.top + pt.y;
|
|
offset.x = offset.y = 0;
|
|
monitor = MonitorFromPoint( cpt, MONITOR_DEFAULTTOPRIMARY );
|
|
}
|
|
else /* CFS_DEFAULT */
|
|
{
|
|
/* Windows places the default IME window in the bottom left */
|
|
HWND target = ctx->hWnd;
|
|
if (!target) target = GetFocus();
|
|
|
|
GetWindowRect( target, &rect );
|
|
rect.top = rect.bottom;
|
|
rect.right = rect.left + pt.x + 20;
|
|
rect.bottom = rect.top + pt.y + 20;
|
|
offset.x = offset.y = 10;
|
|
monitor = MonitorFromWindow( target, MONITOR_DEFAULTTOPRIMARY );
|
|
}
|
|
|
|
if (ctx->cfCompForm.dwStyle == CFS_RECT)
|
|
{
|
|
RECT client = ctx->cfCompForm.rcArea;
|
|
MapWindowPoints( ctx->hWnd, 0, (POINT *)&client, 2 );
|
|
IntersectRect( &rect, &rect, &client );
|
|
DrawTextW( hdc, str, len, &rect, DT_WORDBREAK | DT_CALCRECT );
|
|
}
|
|
|
|
if (ctx->cfCompForm.dwStyle == CFS_DEFAULT)
|
|
{
|
|
/* make sure we are on the desktop */
|
|
mon_info.cbSize = sizeof(mon_info);
|
|
GetMonitorInfoW( monitor, &mon_info );
|
|
|
|
if (rect.bottom > mon_info.rcWork.bottom)
|
|
{
|
|
int shift = rect.bottom - mon_info.rcWork.bottom;
|
|
rect.top -= shift;
|
|
rect.bottom -= shift;
|
|
}
|
|
if (rect.left < 0)
|
|
{
|
|
rect.right -= rect.left;
|
|
rect.left = 0;
|
|
}
|
|
if (rect.right > mon_info.rcWork.right)
|
|
{
|
|
int shift = rect.right - mon_info.rcWork.right;
|
|
rect.left -= shift;
|
|
rect.right -= shift;
|
|
}
|
|
}
|
|
|
|
new_rect = rect;
|
|
OffsetRect( &rect, offset.x - rect.left, offset.y - rect.top );
|
|
DrawTextW( hdc, str, len, &rect, DT_WORDBREAK );
|
|
|
|
if (font) SelectObject( hdc, font );
|
|
free( str );
|
|
}
|
|
|
|
EndPaint( hwnd, &ps );
|
|
ImmUnlockIMC( himc );
|
|
|
|
if (!EqualRect( &rect, &new_rect ))
|
|
SetWindowPos( hwnd, HWND_TOPMOST, new_rect.left, new_rect.top, new_rect.right - new_rect.left,
|
|
new_rect.bottom - new_rect.top, SWP_NOACTIVATE );
|
|
}
|
|
|
|
static void ime_ui_update_window( INPUTCONTEXT *ctx, HWND hwnd )
|
|
{
|
|
COMPOSITIONSTRING *string;
|
|
|
|
if (ctx->hCompStr) string = ImmLockIMCC( ctx->hCompStr );
|
|
else string = NULL;
|
|
|
|
if (!string || string->dwCompStrLen == 0)
|
|
ShowWindow( hwnd, SW_HIDE );
|
|
else
|
|
{
|
|
ShowWindow( hwnd, SW_SHOWNOACTIVATE );
|
|
RedrawWindow( hwnd, NULL, NULL, RDW_ERASENOW | RDW_INVALIDATE );
|
|
}
|
|
|
|
if (string) ImmUnlockIMCC( ctx->hCompStr );
|
|
|
|
ctx->hWnd = GetFocus();
|
|
}
|
|
|
|
static void ime_ui_composition( HIMC himc, HWND hwnd, LPARAM lparam )
|
|
{
|
|
INPUTCONTEXT *ctx;
|
|
if (lparam & GCS_RESULTSTR) return;
|
|
if (!(ctx = ImmLockIMC( himc ))) return;
|
|
ime_ui_update_window( ctx, hwnd );
|
|
ImmUnlockIMC( himc );
|
|
}
|
|
|
|
static void ime_ui_start_composition( HIMC himc, HWND hwnd )
|
|
{
|
|
INPUTCONTEXT *ctx;
|
|
if (!(ctx = ImmLockIMC( himc ))) return;
|
|
ime_ui_update_window( ctx, hwnd );
|
|
ImmUnlockIMC( himc );
|
|
}
|
|
|
|
static UINT ime_set_comp_string( HIMC himc, LPARAM lparam )
|
|
{
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
UINT uMsgCount;
|
|
TRANSMSG TransMsg[10];
|
|
};
|
|
TRANSMSGLIST list;
|
|
} buffer = {.uMsgCount = ARRAY_SIZE(buffer.TransMsg)};
|
|
INPUTCONTEXT *ctx;
|
|
TRANSMSG *msgs;
|
|
HIMCC himcc;
|
|
UINT count;
|
|
|
|
TRACE( "himc %p\n", himc );
|
|
|
|
if (!(ctx = ImmLockIMC( himc ))) return 0;
|
|
|
|
count = ImeToAsciiEx( VK_PROCESSKEY, lparam, NULL, &buffer.list, 0, himc );
|
|
if (!count)
|
|
TRACE( "ImeToAsciiEx returned no messages\n" );
|
|
else if (count >= buffer.uMsgCount)
|
|
WARN( "ImeToAsciiEx returned %#x messages\n", count );
|
|
else if (!(himcc = ImmReSizeIMCC( ctx->hMsgBuf, (ctx->dwNumMsgBuf + count) * sizeof(*msgs) )))
|
|
WARN( "Failed to resize input context message buffer\n" );
|
|
else if (!(msgs = ImmLockIMCC( (ctx->hMsgBuf = himcc) )))
|
|
WARN( "Failed to lock input context message buffer\n" );
|
|
else
|
|
{
|
|
memcpy( msgs + ctx->dwNumMsgBuf, buffer.TransMsg, count * sizeof(*msgs) );
|
|
ImmUnlockIMCC( ctx->hMsgBuf );
|
|
ctx->dwNumMsgBuf += count;
|
|
}
|
|
|
|
ImmUnlockIMC( himc );
|
|
ImmGenerateMessage( himc );
|
|
|
|
return count;
|
|
}
|
|
|
|
static LRESULT ime_ui_notify( HIMC himc, HWND hwnd, WPARAM wparam, LPARAM lparam )
|
|
{
|
|
TRACE( "himc %p, hwnd %p, wparam %s, lparam %#Ix\n", hwnd, himc, debugstr_imn(wparam), lparam );
|
|
|
|
switch (wparam)
|
|
{
|
|
case IMN_WINE_SET_OPEN_STATUS:
|
|
return ImmSetOpenStatus( himc, lparam );
|
|
case IMN_WINE_SET_COMP_STRING:
|
|
return ime_set_comp_string( himc, lparam );
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static LRESULT WINAPI ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
|
|
{
|
|
HIMC himc = (HIMC)GetWindowLongPtrW( hwnd, IMMGWL_IMC );
|
|
|
|
TRACE( "hwnd %p, himc %p, msg %s, wparam %#Ix, lparam %#Ix\n",
|
|
hwnd, himc, debugstr_wm_ime(msg), wparam, lparam );
|
|
|
|
switch (msg)
|
|
{
|
|
case WM_CREATE:
|
|
return TRUE;
|
|
case WM_PAINT:
|
|
ime_ui_paint( himc, hwnd );
|
|
return FALSE;
|
|
case WM_SETFOCUS:
|
|
if (wparam) SetFocus( (HWND)wparam );
|
|
else FIXME( "Received focus, should never have focus\n" );
|
|
break;
|
|
case WM_IME_COMPOSITION:
|
|
ime_ui_composition( himc, hwnd, lparam );
|
|
break;
|
|
case WM_IME_STARTCOMPOSITION:
|
|
ime_ui_start_composition( himc, hwnd );
|
|
break;
|
|
case WM_IME_ENDCOMPOSITION:
|
|
ShowWindow( hwnd, SW_HIDE );
|
|
break;
|
|
case WM_IME_NOTIFY:
|
|
return ime_ui_notify( himc, hwnd, wparam, lparam );
|
|
case WM_IME_CONTROL:
|
|
FIXME( "hwnd %p, himc %p, msg %s, wparam %s, lparam %#Ix stub!\n", hwnd, himc,
|
|
debugstr_wm_ime(msg), debugstr_imc(wparam), lparam );
|
|
return 1;
|
|
}
|
|
|
|
return DefWindowProcW( hwnd, msg, wparam, lparam );
|
|
}
|
|
|
|
static WNDCLASSEXW ime_ui_class =
|
|
{
|
|
.cbSize = sizeof(WNDCLASSEXW),
|
|
.style = CS_GLOBALCLASS | CS_IME | CS_HREDRAW | CS_VREDRAW,
|
|
.lpfnWndProc = ime_ui_window_proc,
|
|
.cbWndExtra = 2 * sizeof(LONG_PTR),
|
|
.lpszClassName = L"Wine IME",
|
|
.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1),
|
|
};
|
|
|
|
BOOL WINAPI ImeInquire( IMEINFO *info, WCHAR *ui_class, DWORD flags )
|
|
{
|
|
TRACE( "info %p, ui_class %p, flags %#lx\n", info, ui_class, flags );
|
|
|
|
ime_ui_class.hInstance = imm32_module;
|
|
ime_ui_class.hCursor = LoadCursorW( NULL, (LPWSTR)IDC_ARROW );
|
|
ime_ui_class.hIcon = LoadIconW( NULL, (LPWSTR)IDI_APPLICATION );
|
|
RegisterClassExW( &ime_ui_class );
|
|
|
|
wcscpy( ui_class, ime_ui_class.lpszClassName );
|
|
memset( info, 0, sizeof(*info) );
|
|
info->dwPrivateDataSize = sizeof(struct ime_private);
|
|
info->fdwProperty = IME_PROP_UNICODE | IME_PROP_AT_CARET;
|
|
info->fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE;
|
|
info->fdwSentenceCaps = IME_SMODE_AUTOMATIC;
|
|
info->fdwUICaps = UI_CAP_2700;
|
|
/* Tell App we cannot accept ImeSetCompositionString calls */
|
|
info->fdwSCSCaps = 0;
|
|
info->fdwSelectCaps = SELECT_CAP_CONVERSION;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL WINAPI ImeDestroy( UINT force )
|
|
{
|
|
TRACE( "force %u\n", force );
|
|
UnregisterClassW( ime_ui_class.lpszClassName, imm32_module );
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL WINAPI ImeSelect( HIMC himc, BOOL select )
|
|
{
|
|
struct ime_private *priv;
|
|
INPUTCONTEXT *ctx;
|
|
|
|
TRACE( "himc %p, select %u\n", himc, select );
|
|
|
|
if (!himc || !select) return TRUE;
|
|
if (!(ctx = ImmLockIMC( himc ))) return FALSE;
|
|
|
|
ImmSetOpenStatus( himc, FALSE );
|
|
|
|
if ((priv = ImmLockIMCC( ctx->hPrivate )))
|
|
{
|
|
memset( priv, 0, sizeof(*priv) );
|
|
ImmUnlockIMCC( ctx->hPrivate );
|
|
}
|
|
|
|
ImmUnlockIMC( himc );
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL WINAPI ImeSetActiveContext( HIMC himc, BOOL flag )
|
|
{
|
|
TRACE( "himc %p, flag %#x stub!\n", himc, flag );
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL WINAPI ImeProcessKey( HIMC himc, UINT vkey, LPARAM lparam, BYTE *state )
|
|
{
|
|
struct ime_driver_call_params params = {.himc = himc, .state = state};
|
|
INPUTCONTEXT *ctx;
|
|
LRESULT ret;
|
|
|
|
TRACE( "himc %p, vkey %#x, lparam %#Ix, state %p\n", himc, vkey, lparam, state );
|
|
|
|
if (!(ctx = ImmLockIMC( himc ))) return FALSE;
|
|
ret = NtUserMessageCall( ctx->hWnd, WINE_IME_PROCESS_KEY, vkey, lparam, ¶ms,
|
|
NtUserImeDriverCall, FALSE );
|
|
ImmUnlockIMC( himc );
|
|
|
|
return ret;
|
|
}
|
|
|
|
UINT WINAPI ImeToAsciiEx( UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, UINT flags, HIMC himc )
|
|
{
|
|
COMPOSITIONSTRING *compstr;
|
|
UINT size, count = 0;
|
|
INPUTCONTEXT *ctx;
|
|
NTSTATUS status;
|
|
|
|
TRACE( "vkey %#x, vsc %#x, state %p, msgs %p, flags %#x, himc %p\n",
|
|
vkey, vsc, state, msgs, flags, himc );
|
|
|
|
if (!(ctx = ImmLockIMC( himc ))) return 0;
|
|
if (!(compstr = ImmLockIMCC( ctx->hCompStr ))) goto done;
|
|
size = max( compstr->dwSize, sizeof(COMPOSITIONSTRING) );
|
|
|
|
do
|
|
{
|
|
struct ime_driver_call_params params = {.himc = himc, .state = state};
|
|
HIMCC himcc;
|
|
|
|
ImmUnlockIMCC( ctx->hCompStr );
|
|
if (!(himcc = ImmReSizeIMCC( ctx->hCompStr, size ))) goto done;
|
|
if (!(compstr = ImmLockIMCC( (ctx->hCompStr = himcc) ))) goto done;
|
|
|
|
params.compstr = compstr;
|
|
status = NtUserMessageCall( ctx->hWnd, WINE_IME_TO_ASCII_EX, vkey, vsc, ¶ms,
|
|
NtUserImeDriverCall, FALSE );
|
|
size = compstr->dwSize;
|
|
} while (status == STATUS_BUFFER_TOO_SMALL);
|
|
|
|
if (status) WARN( "WINE_IME_TO_ASCII_EX returned status %#lx\n", status );
|
|
else
|
|
{
|
|
TRANSMSG status_msg = {.message = ime_set_composition_status( himc, !!compstr->dwCompStrOffset )};
|
|
if (status_msg.message) msgs->TransMsg[count++] = status_msg;
|
|
|
|
if (compstr->dwResultStrOffset)
|
|
{
|
|
const WCHAR *result = (WCHAR *)((BYTE *)compstr + compstr->dwResultStrOffset);
|
|
TRANSMSG msg = {.message = WM_IME_COMPOSITION, .wParam = result[0], .lParam = GCS_RESULTSTR};
|
|
if (compstr->dwResultClauseOffset) msg.lParam |= GCS_RESULTCLAUSE;
|
|
msgs->TransMsg[count++] = msg;
|
|
}
|
|
|
|
if (compstr->dwCompStrOffset)
|
|
{
|
|
const WCHAR *comp = (WCHAR *)((BYTE *)compstr + compstr->dwCompStrOffset);
|
|
TRANSMSG msg = {.message = WM_IME_COMPOSITION, .wParam = comp[0], .lParam = GCS_COMPSTR | GCS_CURSORPOS | GCS_DELTASTART};
|
|
if (compstr->dwCompAttrOffset) msg.lParam |= GCS_COMPATTR;
|
|
if (compstr->dwCompClauseOffset) msg.lParam |= GCS_COMPCLAUSE;
|
|
else msg.lParam |= CS_INSERTCHAR|CS_NOMOVECARET;
|
|
msgs->TransMsg[count++] = msg;
|
|
}
|
|
}
|
|
|
|
ImmUnlockIMCC( ctx->hCompStr );
|
|
|
|
done:
|
|
if (count >= msgs->uMsgCount) FIXME( "More than %u messages queued, messages possibly lost\n", msgs->uMsgCount );
|
|
else TRACE( "Returning %u messages queued\n", count );
|
|
ImmUnlockIMC( himc );
|
|
return count;
|
|
}
|
|
|
|
BOOL WINAPI ImeConfigure( HKL hkl, HWND hwnd, DWORD mode, void *data )
|
|
{
|
|
FIXME( "hkl %p, hwnd %p, mode %lu, data %p stub!\n", hkl, hwnd, mode, data );
|
|
SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
|
|
return FALSE;
|
|
}
|
|
|
|
DWORD WINAPI ImeConversionList( HIMC himc, const WCHAR *source, CANDIDATELIST *dest, DWORD dest_len, UINT flag )
|
|
{
|
|
FIXME( "himc %p, source %s, dest %p, dest_len %lu, flag %#x stub!\n",
|
|
himc, debugstr_w(source), dest, dest_len, flag );
|
|
SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
|
|
return 0;
|
|
}
|
|
|
|
BOOL WINAPI ImeSetCompositionString( HIMC himc, DWORD index, const void *comp, DWORD comp_len,
|
|
const void *read, DWORD read_len )
|
|
{
|
|
INPUTCONTEXT *ctx;
|
|
|
|
FIXME( "himc %p, index %lu, comp %p, comp_len %lu, read %p, read_len %lu semi-stub!\n",
|
|
himc, index, comp, comp_len, read, read_len );
|
|
if (read && read_len) FIXME( "Read string unimplemented\n" );
|
|
if (index != SCS_SETSTR && index != SCS_CHANGECLAUSE && index != SCS_CHANGEATTR) return FALSE;
|
|
|
|
if (!(ctx = ImmLockIMC( himc ))) return FALSE;
|
|
|
|
if (index != SCS_SETSTR)
|
|
FIXME( "index %#lx not implemented\n", index );
|
|
else
|
|
{
|
|
UINT msg, flags = GCS_COMPSTR | GCS_COMPCLAUSE | GCS_COMPATTR | GCS_DELTASTART | GCS_CURSORPOS;
|
|
WCHAR wparam = comp && comp_len >= sizeof(WCHAR) ? *(WCHAR *)comp : 0;
|
|
input_context_set_comp_str( ctx, comp, comp_len / sizeof(WCHAR) );
|
|
if ((msg = ime_set_composition_status( himc, TRUE ))) ime_send_message( himc, msg, 0, 0 );
|
|
ime_send_message( himc, WM_IME_COMPOSITION, wparam, flags );
|
|
}
|
|
|
|
ImmUnlockIMC( himc );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL WINAPI NotifyIME( HIMC himc, DWORD action, DWORD index, DWORD value )
|
|
{
|
|
struct ime_private *priv;
|
|
INPUTCONTEXT *ctx;
|
|
UINT msg;
|
|
|
|
TRACE( "himc %p, action %#lx, index %#lx, value %#lx stub!\n", himc, action, index, value );
|
|
|
|
if (!(ctx = ImmLockIMC( himc ))) return FALSE;
|
|
|
|
switch (action)
|
|
{
|
|
case NI_CONTEXTUPDATED:
|
|
switch (value)
|
|
{
|
|
case IMC_SETCOMPOSITIONFONT:
|
|
if ((priv = ImmLockIMCC( ctx->hPrivate )))
|
|
{
|
|
if (priv->hfont) DeleteObject( priv->hfont );
|
|
priv->hfont = CreateFontIndirectW( &ctx->lfFont.W );
|
|
ImmUnlockIMCC( ctx->hPrivate );
|
|
}
|
|
break;
|
|
case IMC_SETOPENSTATUS:
|
|
if (!ctx->fOpen)
|
|
{
|
|
input_context_set_comp_str( ctx, NULL, 0 );
|
|
if ((msg = ime_set_composition_status( himc, FALSE ))) ime_send_message( himc, msg, 0, 0 );
|
|
}
|
|
NtUserNotifyIMEStatus( ctx->hWnd, ctx->fOpen );
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case NI_COMPOSITIONSTR:
|
|
switch (index)
|
|
{
|
|
case CPS_COMPLETE:
|
|
{
|
|
COMPOSITIONSTRING *compstr;
|
|
|
|
if (!(compstr = ImmLockIMCC( ctx->hCompStr )))
|
|
WARN( "Failed to lock input context composition string\n" );
|
|
else
|
|
{
|
|
WCHAR wchr = *(WCHAR *)((BYTE *)compstr + compstr->dwCompStrOffset);
|
|
COMPOSITIONSTRING tmp = *compstr;
|
|
UINT flags = 0;
|
|
|
|
memset( compstr, 0, sizeof(*compstr) );
|
|
compstr->dwSize = tmp.dwSize;
|
|
compstr->dwResultStrLen = tmp.dwCompStrLen;
|
|
compstr->dwResultStrOffset = tmp.dwCompStrOffset;
|
|
compstr->dwResultClauseLen = tmp.dwCompClauseLen;
|
|
compstr->dwResultClauseOffset = tmp.dwCompClauseOffset;
|
|
ImmUnlockIMCC( ctx->hCompStr );
|
|
|
|
if (tmp.dwCompStrLen) flags |= GCS_RESULTSTR;
|
|
if (tmp.dwCompClauseLen) flags |= GCS_RESULTCLAUSE;
|
|
if (flags) ime_send_message( himc, WM_IME_COMPOSITION, wchr, flags );
|
|
}
|
|
|
|
ImmSetOpenStatus( himc, FALSE );
|
|
break;
|
|
}
|
|
case CPS_CANCEL:
|
|
input_context_set_comp_str( ctx, NULL, 0 );
|
|
ImmSetOpenStatus( himc, FALSE );
|
|
break;
|
|
default:
|
|
FIXME( "himc %p, action %#lx, index %#lx, value %#lx stub!\n", himc, action, index, value );
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
FIXME( "himc %p, action %#lx, index %#lx, value %#lx stub!\n", himc, action, index, value );
|
|
break;
|
|
}
|
|
|
|
ImmUnlockIMC( himc );
|
|
return TRUE;
|
|
}
|
|
|
|
LRESULT WINAPI ImeEscape( HIMC himc, UINT escape, void *data )
|
|
{
|
|
FIXME( "himc %p, escape %#x, data %p stub!\n", himc, escape, data );
|
|
SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
|
|
return 0;
|
|
}
|
|
|
|
DWORD WINAPI ImeGetImeMenuItems( HIMC himc, DWORD flags, DWORD type, IMEMENUITEMINFOW *parent,
|
|
IMEMENUITEMINFOW *menu, DWORD size )
|
|
{
|
|
FIXME( "himc %p, flags %#lx, type %lu, parent %p, menu %p, size %#lx stub!\n",
|
|
himc, flags, type, parent, menu, size );
|
|
SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
|
|
return 0;
|
|
}
|
|
|
|
BOOL WINAPI ImeRegisterWord( const WCHAR *reading, DWORD style, const WCHAR *string )
|
|
{
|
|
FIXME( "reading %s, style %lu, string %s stub!\n", debugstr_w(reading), style, debugstr_w(string) );
|
|
SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
|
|
return FALSE;
|
|
}
|
|
|
|
UINT WINAPI ImeGetRegisterWordStyle( UINT item, STYLEBUFW *style )
|
|
{
|
|
FIXME( "item %u, style %p stub!\n", item, style );
|
|
SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
|
|
return 0;
|
|
}
|
|
|
|
BOOL WINAPI ImeUnregisterWord( const WCHAR *reading, DWORD style, const WCHAR *string )
|
|
{
|
|
FIXME( "reading %s, style %lu, string %s stub!\n", debugstr_w(reading), style, debugstr_w(string) );
|
|
SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
|
|
return FALSE;
|
|
}
|
|
|
|
UINT WINAPI ImeEnumRegisterWord( REGISTERWORDENUMPROCW proc, const WCHAR *reading, DWORD style,
|
|
const WCHAR *string, void *data )
|
|
{
|
|
FIXME( "proc %p, reading %s, style %lu, string %s, data %p stub!\n",
|
|
proc, debugstr_w(reading), style, debugstr_w(string), data );
|
|
SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
|
|
return 0;
|
|
}
|