mirror of
https://gitlab.winehq.org/wine/wine.git
synced 2025-08-29 18:53:55 +02:00
Tidy Cauldron (2708320), a Unity game, uses EnumWindows() to find the first window in the current thread and maximizes the window when changing to windowed mode. However, before this patch, the IME UI window and the DXGI fallback device window are on top of the game window at creation and thus they could get maximized instead. This causes the game window to lose focus and freeze.
8196 lines
302 KiB
C
8196 lines
302 KiB
C
/*
|
|
* Unit tests for imm32
|
|
*
|
|
* Copyright (c) 2008 Michael Jung
|
|
*
|
|
* 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 "ntstatus.h"
|
|
#define WIN32_NO_STATUS
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
|
|
#include "wine/test.h"
|
|
#include "objbase.h"
|
|
#include "winuser.h"
|
|
#include "wingdi.h"
|
|
#include "imm.h"
|
|
#include "immdev.h"
|
|
|
|
#include "ime_test.h"
|
|
|
|
static const char *debugstr_wm_ime( UINT msg )
|
|
{
|
|
switch (msg)
|
|
{
|
|
case WM_IME_STARTCOMPOSITION: return "WM_IME_STARTCOMPOSITION";
|
|
case WM_IME_ENDCOMPOSITION: return "WM_IME_ENDCOMPOSITION";
|
|
case WM_IME_COMPOSITION: return "WM_IME_COMPOSITION";
|
|
case WM_IME_SETCONTEXT: return "WM_IME_SETCONTEXT";
|
|
case WM_IME_NOTIFY: return "WM_IME_NOTIFY";
|
|
case WM_IME_CONTROL: return "WM_IME_CONTROL";
|
|
case WM_IME_COMPOSITIONFULL: return "WM_IME_COMPOSITIONFULL";
|
|
case WM_IME_SELECT: return "WM_IME_SELECT";
|
|
case WM_IME_CHAR: return "WM_IME_CHAR";
|
|
case WM_IME_REQUEST: return "WM_IME_REQUEST";
|
|
case WM_IME_KEYDOWN: return "WM_IME_KEYDOWN";
|
|
case WM_IME_KEYUP: return "WM_IME_KEYUP";
|
|
default: return wine_dbg_sprintf( "%#x", msg );
|
|
}
|
|
}
|
|
|
|
static const char *debugstr_ok( const char *cond )
|
|
{
|
|
int c, n = 0;
|
|
/* skip possible casts */
|
|
while ((c = *cond++))
|
|
{
|
|
if (c == '(') n++;
|
|
if (!n) break;
|
|
if (c == ')') n--;
|
|
}
|
|
if (!strchr( cond - 1, '(' )) return wine_dbg_sprintf( "got %s", cond - 1 );
|
|
return wine_dbg_sprintf( "%.*s returned", (int)strcspn( cond - 1, "( " ), cond - 1 );
|
|
}
|
|
|
|
#define ok_eq( e, r, t, f, ... ) \
|
|
do \
|
|
{ \
|
|
t v = (r); \
|
|
ok( v == (e), "%s " f "\n", debugstr_ok( #r ), v, ##__VA_ARGS__ ); \
|
|
} while (0)
|
|
#define ok_ne( e, r, t, f, ... ) \
|
|
do \
|
|
{ \
|
|
t v = (r); \
|
|
ok( v != (e), "%s " f "\n", debugstr_ok( #r ), v, ##__VA_ARGS__ ); \
|
|
} while (0)
|
|
#define ok_wcs( e, r ) \
|
|
do \
|
|
{ \
|
|
const WCHAR *v = (r); \
|
|
ok( !wcscmp( v, (e) ), "%s %s\n", debugstr_ok(#r), debugstr_w(v) ); \
|
|
} while (0)
|
|
#define ok_str( e, r ) \
|
|
do \
|
|
{ \
|
|
const char *v = (r); \
|
|
ok( !strcmp( v, (e) ), "%s %s\n", debugstr_ok(#r), debugstr_a(v) ); \
|
|
} while (0)
|
|
#define ok_ret( e, r ) ok_eq( e, r, UINT_PTR, "%Iu, error %ld", GetLastError() )
|
|
|
|
BOOL WINAPI ImmSetActiveContext(HWND, HIMC, BOOL);
|
|
|
|
static BOOL (WINAPI *pImmAssociateContextEx)(HWND,HIMC,DWORD);
|
|
static UINT (WINAPI *pNtUserAssociateInputContext)(HWND,HIMC,ULONG);
|
|
static BOOL (WINAPI *pImmIsUIMessageA)(HWND,UINT,WPARAM,LPARAM);
|
|
static UINT (WINAPI *pSendInput) (UINT, INPUT*, size_t);
|
|
|
|
extern BOOL WINAPI ImmFreeLayout(HKL);
|
|
extern BOOL WINAPI ImmLoadIME(HKL);
|
|
extern BOOL WINAPI ImmActivateLayout(HKL);
|
|
|
|
#define check_member_( file, line, val, exp, fmt, member ) \
|
|
ok_(file, line)( (val).member == (exp).member, "got " #member " " fmt "\n", (val).member )
|
|
#define check_member( val, exp, fmt, member ) \
|
|
check_member_( __FILE__, __LINE__, val, exp, fmt, member )
|
|
|
|
#define check_member_wstr_( file, line, val, exp, member ) \
|
|
ok_(file, line)( !wcscmp( (val).member, (exp).member ), "got " #member " %s\n", \
|
|
debugstr_w((val).member) )
|
|
#define check_member_wstr( val, exp, member ) \
|
|
check_member_wstr_( __FILE__, __LINE__, val, exp, member )
|
|
|
|
#define check_member_str_( file, line, val, exp, member ) \
|
|
ok_(file, line)( !strcmp( (val).member, (exp).member ), "got " #member " %s\n", \
|
|
debugstr_a((val).member) )
|
|
#define check_member_str( val, exp, member ) \
|
|
check_member_str_( __FILE__, __LINE__, val, exp, member )
|
|
|
|
#define check_member_point_( file, line, val, exp, member ) \
|
|
ok_(file, line)( !memcmp( &(val).member, &(exp).member, sizeof(POINT) ), \
|
|
"got " #member " %s\n", wine_dbgstr_point( &(val).member ) )
|
|
#define check_member_point( val, exp, member ) \
|
|
check_member_point_( __FILE__, __LINE__, val, exp, member )
|
|
|
|
#define check_member_rect_( file, line, val, exp, member ) \
|
|
ok_(file, line)( !memcmp( &(val).member, &(exp).member, sizeof(RECT) ), \
|
|
"got " #member " %s\n", wine_dbgstr_rect( &(val).member ) )
|
|
#define check_member_rect( val, exp, member ) \
|
|
check_member_rect_( __FILE__, __LINE__, val, exp, member )
|
|
|
|
#define check_composition_string( a, b ) check_composition_string_( __LINE__, a, b )
|
|
static void check_composition_string_( int line, COMPOSITIONSTRING *string, const COMPOSITIONSTRING *expect )
|
|
{
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwSize );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwCompReadAttrLen );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwCompReadAttrOffset );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwCompReadClauseLen );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwCompReadClauseOffset );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwCompReadStrLen );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwCompReadStrOffset );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwCompAttrLen );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwCompAttrOffset );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwCompClauseLen );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwCompClauseOffset );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwCompStrLen );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwCompStrOffset );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwCursorPos );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwDeltaStart );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwResultReadClauseLen );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwResultReadClauseOffset );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwResultReadStrLen );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwResultReadStrOffset );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwResultClauseLen );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwResultClauseOffset );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwResultStrLen );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwResultStrOffset );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwPrivateSize );
|
|
check_member_( __FILE__, line, *string, *expect, "%lu", dwPrivateOffset );
|
|
}
|
|
|
|
#define check_candidate_list( a, b ) check_candidate_list_( __LINE__, a, b, TRUE )
|
|
static void check_candidate_list_( int line, CANDIDATELIST *list, const CANDIDATELIST *expect, BOOL unicode )
|
|
{
|
|
UINT i;
|
|
|
|
check_member_( __FILE__, line, *list, *expect, "%lu", dwSize );
|
|
check_member_( __FILE__, line, *list, *expect, "%lu", dwStyle );
|
|
check_member_( __FILE__, line, *list, *expect, "%lu", dwCount );
|
|
check_member_( __FILE__, line, *list, *expect, "%lu", dwSelection );
|
|
check_member_( __FILE__, line, *list, *expect, "%lu", dwPageStart );
|
|
check_member_( __FILE__, line, *list, *expect, "%lu", dwPageSize );
|
|
for (i = 0; i < list->dwCount && i < expect->dwCount; ++i)
|
|
{
|
|
void *list_str = (BYTE *)list + list->dwOffset[i], *expect_str = (BYTE *)expect + expect->dwOffset[i];
|
|
check_member_( __FILE__, line, *list, *expect, "%lu", dwOffset[i] );
|
|
if (unicode) ok_( __FILE__, line )( !wcscmp( list_str, expect_str ), "got %s\n", debugstr_w(list_str) );
|
|
else ok_( __FILE__, line )( !strcmp( list_str, expect_str ), "got %s\n", debugstr_a(list_str) );
|
|
}
|
|
}
|
|
|
|
#define check_candidate_form( a, b ) check_candidate_form_( __LINE__, a, b )
|
|
static void check_candidate_form_( int line, CANDIDATEFORM *form, const CANDIDATEFORM *expect )
|
|
{
|
|
check_member_( __FILE__, line, *form, *expect, "%#lx", dwIndex );
|
|
check_member_( __FILE__, line, *form, *expect, "%#lx", dwStyle );
|
|
check_member_point_( __FILE__, line, *form, *expect, ptCurrentPos );
|
|
check_member_rect_( __FILE__, line, *form, *expect, rcArea );
|
|
}
|
|
|
|
#define check_composition_form( a, b ) check_composition_form_( __LINE__, a, b )
|
|
static void check_composition_form_( int line, COMPOSITIONFORM *form, const COMPOSITIONFORM *expect )
|
|
{
|
|
check_member_( __FILE__, line, *form, *expect, "%#lx", dwStyle );
|
|
check_member_point_( __FILE__, line, *form, *expect, ptCurrentPos );
|
|
check_member_rect_( __FILE__, line, *form, *expect, rcArea );
|
|
}
|
|
|
|
#define check_logfont_w( a, b ) check_logfont_w_( __LINE__, a, b )
|
|
static void check_logfont_w_( int line, LOGFONTW *font, const LOGFONTW *expect )
|
|
{
|
|
check_member_( __FILE__, line, *font, *expect, "%lu", lfHeight );
|
|
check_member_( __FILE__, line, *font, *expect, "%lu", lfWidth );
|
|
check_member_( __FILE__, line, *font, *expect, "%lu", lfEscapement );
|
|
check_member_( __FILE__, line, *font, *expect, "%lu", lfOrientation );
|
|
check_member_( __FILE__, line, *font, *expect, "%lu", lfWeight );
|
|
check_member_( __FILE__, line, *font, *expect, "%u", lfItalic );
|
|
check_member_( __FILE__, line, *font, *expect, "%u", lfUnderline );
|
|
check_member_( __FILE__, line, *font, *expect, "%u", lfStrikeOut );
|
|
check_member_( __FILE__, line, *font, *expect, "%u", lfCharSet );
|
|
check_member_( __FILE__, line, *font, *expect, "%u", lfOutPrecision );
|
|
check_member_( __FILE__, line, *font, *expect, "%u", lfClipPrecision );
|
|
check_member_( __FILE__, line, *font, *expect, "%u", lfQuality );
|
|
check_member_( __FILE__, line, *font, *expect, "%u", lfPitchAndFamily );
|
|
check_member_wstr_( __FILE__, line, *font, *expect, lfFaceName );
|
|
}
|
|
|
|
#define check_logfont_a( a, b ) check_logfont_a_( __LINE__, a, b )
|
|
static void check_logfont_a_( int line, LOGFONTA *font, const LOGFONTA *expect )
|
|
{
|
|
check_member_( __FILE__, line, *font, *expect, "%lu", lfHeight );
|
|
check_member_( __FILE__, line, *font, *expect, "%lu", lfWidth );
|
|
check_member_( __FILE__, line, *font, *expect, "%lu", lfEscapement );
|
|
check_member_( __FILE__, line, *font, *expect, "%lu", lfOrientation );
|
|
check_member_( __FILE__, line, *font, *expect, "%lu", lfWeight );
|
|
check_member_( __FILE__, line, *font, *expect, "%u", lfItalic );
|
|
check_member_( __FILE__, line, *font, *expect, "%u", lfUnderline );
|
|
check_member_( __FILE__, line, *font, *expect, "%u", lfStrikeOut );
|
|
check_member_( __FILE__, line, *font, *expect, "%u", lfCharSet );
|
|
check_member_( __FILE__, line, *font, *expect, "%u", lfOutPrecision );
|
|
check_member_( __FILE__, line, *font, *expect, "%u", lfClipPrecision );
|
|
check_member_( __FILE__, line, *font, *expect, "%u", lfQuality );
|
|
check_member_( __FILE__, line, *font, *expect, "%u", lfPitchAndFamily );
|
|
check_member_str_( __FILE__, line, *font, *expect, lfFaceName );
|
|
}
|
|
|
|
#define DEFINE_EXPECT(func) \
|
|
static BOOL expect_ ## func = FALSE, called_ ## func = FALSE, enabled_ ## func = FALSE
|
|
|
|
#define SET_EXPECT(func) \
|
|
expect_ ## func = TRUE
|
|
|
|
#define CHECK_EXPECT2(func) \
|
|
do { \
|
|
if (enabled_ ## func) {\
|
|
ok(expect_ ##func, "unexpected call " #func "\n"); \
|
|
called_ ## func = TRUE; \
|
|
} \
|
|
}while(0)
|
|
|
|
#define CHECK_EXPECT(func) \
|
|
do { \
|
|
CHECK_EXPECT2(func); \
|
|
expect_ ## func = FALSE; \
|
|
}while(0)
|
|
|
|
#define CHECK_CALLED(func) \
|
|
do { \
|
|
ok(called_ ## func, "expected " #func "\n"); \
|
|
expect_ ## func = called_ ## func = FALSE; \
|
|
}while(0)
|
|
|
|
#define SET_ENABLE(func, val) \
|
|
enabled_ ## func = (val)
|
|
|
|
DEFINE_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
|
|
DEFINE_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
|
|
|
|
#define process_messages() process_messages_(0)
|
|
static void process_messages_(HWND hwnd)
|
|
{
|
|
MSG msg;
|
|
|
|
while (PeekMessageA( &msg, hwnd, 0, 0, PM_REMOVE ))
|
|
{
|
|
TranslateMessage( &msg );
|
|
DispatchMessageA( &msg );
|
|
}
|
|
}
|
|
|
|
/* try to make sure pending X events have been processed before continuing */
|
|
#define flush_events() flush_events_( 100, 200 )
|
|
static void flush_events_( int min_timeout, int max_timeout )
|
|
{
|
|
DWORD time = GetTickCount() + max_timeout;
|
|
MSG msg;
|
|
|
|
while (max_timeout > 0)
|
|
{
|
|
if (MsgWaitForMultipleObjects( 0, NULL, FALSE, min_timeout, QS_ALLINPUT ) == WAIT_TIMEOUT) break;
|
|
while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE ))
|
|
{
|
|
TranslateMessage( &msg );
|
|
DispatchMessageA( &msg );
|
|
}
|
|
max_timeout = time - GetTickCount();
|
|
}
|
|
}
|
|
|
|
#define ime_trace( msg, ... ) if (winetest_debug > 1) trace( "%04lx:%s " msg, GetCurrentThreadId(), __func__, ## __VA_ARGS__ )
|
|
|
|
static BOOL ImeSelect_init_status;
|
|
static BOOL todo_ImeInquire;
|
|
DEFINE_EXPECT( ImeInquire );
|
|
static BOOL todo_ImeDestroy;
|
|
DEFINE_EXPECT( ImeDestroy );
|
|
DEFINE_EXPECT( ImeEscape );
|
|
DEFINE_EXPECT( ImeEnumRegisterWord );
|
|
DEFINE_EXPECT( ImeRegisterWord );
|
|
DEFINE_EXPECT( ImeGetRegisterWordStyle );
|
|
DEFINE_EXPECT( ImeUnregisterWord );
|
|
static BOOL todo_ImeSetCompositionString;
|
|
DEFINE_EXPECT( ImeSetCompositionString );
|
|
static BOOL todo_IME_DLL_PROCESS_ATTACH;
|
|
DEFINE_EXPECT( IME_DLL_PROCESS_ATTACH );
|
|
static BOOL todo_IME_DLL_PROCESS_DETACH;
|
|
DEFINE_EXPECT( IME_DLL_PROCESS_DETACH );
|
|
|
|
static IMEINFO ime_info;
|
|
static UINT ime_count;
|
|
static WCHAR ime_path[MAX_PATH];
|
|
static HIMC default_himc;
|
|
static HKL default_hkl, wineime_hkl;
|
|
static HKL expect_ime = (HKL)(int)0xe020047f;
|
|
|
|
enum ime_function
|
|
{
|
|
IME_SELECT = 1,
|
|
IME_NOTIFY,
|
|
IME_PROCESS_KEY,
|
|
IME_TO_ASCII_EX,
|
|
IME_SET_ACTIVE_CONTEXT,
|
|
MSG_IME_UI,
|
|
MSG_TEST_WIN,
|
|
};
|
|
|
|
struct ime_call
|
|
{
|
|
HKL hkl;
|
|
HIMC himc;
|
|
enum ime_function func;
|
|
|
|
WCHAR comp[16];
|
|
WCHAR result[16];
|
|
|
|
union
|
|
{
|
|
int select;
|
|
struct
|
|
{
|
|
int action;
|
|
int index;
|
|
int value;
|
|
} notify;
|
|
struct
|
|
{
|
|
WORD vkey;
|
|
LPARAM lparam;
|
|
} process_key;
|
|
struct
|
|
{
|
|
UINT vkey;
|
|
UINT vsc;
|
|
UINT flags;
|
|
} to_ascii_ex;
|
|
struct
|
|
{
|
|
int flag;
|
|
} set_active_context;
|
|
struct
|
|
{
|
|
UINT msg;
|
|
WPARAM wparam;
|
|
LPARAM lparam;
|
|
} message;
|
|
};
|
|
|
|
BOOL todo;
|
|
BOOL broken;
|
|
BOOL flaky_himc;
|
|
BOOL todo_value;
|
|
BOOL todo_himc;
|
|
};
|
|
|
|
struct ime_call empty_sequence[] = {{0}};
|
|
static struct ime_call ime_calls[1024];
|
|
static ULONG ime_call_count;
|
|
|
|
#define ok_call( a, b ) ok_call_( __FILE__, __LINE__, a, b )
|
|
static int ok_call_( const char *file, int line, const struct ime_call *expected, const struct ime_call *received )
|
|
{
|
|
int ret;
|
|
|
|
if ((ret = expected->func - received->func)) goto done;
|
|
/* Wine doesn't allocate HIMC in a deterministic order, ignore them when they are enumerated */
|
|
if (expected->flaky_himc && (ret = !!(UINT_PTR)expected->himc - !!(UINT_PTR)received->himc)) goto done;
|
|
if (!expected->flaky_himc && (ret = (UINT_PTR)expected->himc - (UINT_PTR)received->himc))
|
|
{
|
|
/* on some Wine configurations the IME UI doesn't get an HIMC */
|
|
if (!winetest_platform_is_wine || !expected->todo_himc) goto done;
|
|
else todo_wine ok( 0, "got himc %p\n", received->himc );
|
|
}
|
|
|
|
if ((ret = (UINT)(UINT_PTR)expected->hkl - (UINT)(UINT_PTR)received->hkl)) goto done;
|
|
switch (expected->func)
|
|
{
|
|
case IME_SELECT:
|
|
if ((ret = expected->select - received->select)) goto done;
|
|
break;
|
|
case IME_NOTIFY:
|
|
if ((ret = expected->notify.action - received->notify.action)) goto done;
|
|
if ((ret = expected->notify.index - received->notify.index)) goto done;
|
|
if ((ret = expected->notify.value - received->notify.value)) goto done;
|
|
break;
|
|
case IME_PROCESS_KEY:
|
|
if ((ret = expected->process_key.vkey - received->process_key.vkey)) goto done;
|
|
if ((ret = expected->process_key.lparam - received->process_key.lparam)) goto done;
|
|
break;
|
|
case IME_TO_ASCII_EX:
|
|
if ((ret = expected->to_ascii_ex.vkey - received->to_ascii_ex.vkey)) goto done;
|
|
if ((ret = expected->to_ascii_ex.vsc - received->to_ascii_ex.vsc)) goto done;
|
|
if ((ret = expected->to_ascii_ex.flags - received->to_ascii_ex.flags)) goto done;
|
|
break;
|
|
case IME_SET_ACTIVE_CONTEXT:
|
|
if ((ret = expected->set_active_context.flag - received->set_active_context.flag)) goto done;
|
|
break;
|
|
case MSG_IME_UI:
|
|
case MSG_TEST_WIN:
|
|
if ((ret = expected->message.msg - received->message.msg)) goto done;
|
|
if ((ret = (expected->message.wparam - received->message.wparam))) goto done;
|
|
if ((ret = (expected->message.lparam - received->message.lparam))) goto done;
|
|
if ((ret = wcscmp( expected->comp, received->comp ))) goto done;
|
|
if ((ret = wcscmp( expected->result, received->result ))) goto done;
|
|
break;
|
|
}
|
|
|
|
done:
|
|
if (ret && broken( expected->broken )) return ret;
|
|
|
|
switch (received->func)
|
|
{
|
|
case IME_SELECT:
|
|
todo_wine_if( expected->todo || expected->todo_value )
|
|
ok_(file, line)( !ret, "got hkl %p, himc %p, IME_SELECT select %u\n", received->hkl, received->himc, received->select );
|
|
return ret;
|
|
case IME_NOTIFY:
|
|
todo_wine_if( expected->todo || expected->todo_value )
|
|
ok_(file, line)( !ret, "got hkl %p, himc %p, IME_NOTIFY action %#x, index %#x, value %#x\n",
|
|
received->hkl, received->himc, received->notify.action, received->notify.index,
|
|
received->notify.value );
|
|
return ret;
|
|
case IME_PROCESS_KEY:
|
|
todo_wine_if( expected->todo || expected->todo_value )
|
|
ok_(file, line)( !ret, "got hkl %p, himc %p, IME_PROCESS_KEY vkey %#x, lparam %#Ix\n",
|
|
received->hkl, received->himc, received->process_key.vkey, received->process_key.lparam );
|
|
return ret;
|
|
case IME_TO_ASCII_EX:
|
|
todo_wine_if( expected->todo || expected->todo_value )
|
|
ok_(file, line)( !ret, "got hkl %p, himc %p, IME_TO_ASCII_EX vkey %#x, vsc %#x, flags %#x\n",
|
|
received->hkl, received->himc, received->to_ascii_ex.vkey, received->to_ascii_ex.vsc,
|
|
received->to_ascii_ex.flags );
|
|
return ret;
|
|
case IME_SET_ACTIVE_CONTEXT:
|
|
todo_wine_if( expected->todo || expected->todo_value )
|
|
ok_(file, line)( !ret, "got hkl %p, himc %p, IME_SET_ACTIVE_CONTEXT flag %u\n", received->hkl, received->himc,
|
|
received->set_active_context.flag );
|
|
return ret;
|
|
case MSG_IME_UI:
|
|
todo_wine_if( expected->todo || expected->todo_value )
|
|
ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_IME_UI msg %s, wparam %#Ix, lparam %#Ix\n", received->hkl,
|
|
received->himc, debugstr_wm_ime(received->message.msg), received->message.wparam, received->message.lparam );
|
|
return ret;
|
|
case MSG_TEST_WIN:
|
|
todo_wine_if( expected->todo || expected->todo_value )
|
|
ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_TEST_WIN msg %s, wparam %#Ix, lparam %#Ix, comp %s, result %s\n", received->hkl,
|
|
received->himc, debugstr_wm_ime(received->message.msg), received->message.wparam, received->message.lparam,
|
|
debugstr_w(received->comp), debugstr_w(received->result) );
|
|
return ret;
|
|
}
|
|
|
|
switch (expected->func)
|
|
{
|
|
case IME_SELECT:
|
|
todo_wine_if( expected->todo || expected->todo_value )
|
|
ok_(file, line)( !ret, "hkl %p, himc %p, IME_SELECT select %u\n", expected->hkl, expected->himc, expected->select );
|
|
break;
|
|
case IME_NOTIFY:
|
|
todo_wine_if( expected->todo || expected->todo_value )
|
|
ok_(file, line)( !ret, "hkl %p, himc %p, IME_NOTIFY action %#x, index %#x, value %#x\n",
|
|
expected->hkl, expected->himc, expected->notify.action, expected->notify.index,
|
|
expected->notify.value );
|
|
break;
|
|
case IME_PROCESS_KEY:
|
|
todo_wine_if( expected->todo || expected->todo_value )
|
|
ok_(file, line)( !ret, "hkl %p, himc %p, IME_PROCESS_KEY vkey %#x, lparam %#Ix\n",
|
|
expected->hkl, expected->himc, expected->process_key.vkey, expected->process_key.lparam );
|
|
break;
|
|
case IME_TO_ASCII_EX:
|
|
todo_wine_if( expected->todo || expected->todo_value )
|
|
ok_(file, line)( !ret, "hkl %p, himc %p, IME_TO_ASCII_EX vkey %#x, vsc %#x, flags %#x\n",
|
|
expected->hkl, expected->himc, expected->to_ascii_ex.vkey, expected->to_ascii_ex.vsc,
|
|
expected->to_ascii_ex.flags );
|
|
break;
|
|
case IME_SET_ACTIVE_CONTEXT:
|
|
todo_wine_if( expected->todo || expected->todo_value )
|
|
ok_(file, line)( !ret, "hkl %p, himc %p, IME_SET_ACTIVE_CONTEXT flag %u\n", expected->hkl, expected->himc,
|
|
expected->set_active_context.flag );
|
|
break;
|
|
case MSG_IME_UI:
|
|
todo_wine_if( expected->todo || expected->todo_value )
|
|
ok_(file, line)( !ret, "hkl %p, himc %p, MSG_IME_UI msg %s, wparam %#Ix, lparam %#Ix\n", expected->hkl,
|
|
expected->himc, debugstr_wm_ime(expected->message.msg), expected->message.wparam, expected->message.lparam );
|
|
break;
|
|
case MSG_TEST_WIN:
|
|
todo_wine_if( expected->todo || expected->todo_value )
|
|
ok_(file, line)( !ret, "hkl %p, himc %p, MSG_TEST_WIN msg %s, wparam %#Ix, lparam %#Ix, comp %s, result %s\n", expected->hkl,
|
|
expected->himc, debugstr_wm_ime(expected->message.msg), expected->message.wparam, expected->message.lparam,
|
|
debugstr_w(expected->comp), debugstr_w(expected->result) );
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define ok_seq( a ) ok_seq_( __FILE__, __LINE__, a, #a )
|
|
static void ok_seq_( const char *file, int line, const struct ime_call *expected, const char *context )
|
|
{
|
|
const struct ime_call *received = ime_calls;
|
|
UINT i = 0, ret;
|
|
|
|
while (expected->func || received->func)
|
|
{
|
|
winetest_push_context( "%u%s%s", i++, !expected->func ? " (spurious)" : "",
|
|
!received->func ? " (missing)" : "" );
|
|
ret = ok_call_( file, line, expected, received );
|
|
if (ret && expected->todo && expected->func &&
|
|
!strcmp( winetest_platform, "wine" ))
|
|
expected++;
|
|
else if (ret && broken(expected->broken))
|
|
expected++;
|
|
else
|
|
{
|
|
if (expected->func) expected++;
|
|
if (received->func) received++;
|
|
}
|
|
winetest_pop_context();
|
|
}
|
|
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
}
|
|
|
|
static BOOL check_WM_SHOWWINDOW;
|
|
static BOOL ignore_IME_NOTIFY;
|
|
static BOOL ignore_WM_IME_NOTIFY;
|
|
static BOOL ignore_WM_IME_REQUEST;
|
|
|
|
static BOOL ignore_message( UINT msg, WPARAM wparam )
|
|
{
|
|
switch (msg)
|
|
{
|
|
case WM_IME_NOTIFY:
|
|
if (ignore_WM_IME_NOTIFY) return TRUE;
|
|
return wparam > IMN_PRIVATE;
|
|
case WM_IME_REQUEST:
|
|
if (ignore_WM_IME_REQUEST) return TRUE;
|
|
return FALSE;
|
|
case WM_IME_STARTCOMPOSITION:
|
|
case WM_IME_ENDCOMPOSITION:
|
|
case WM_IME_COMPOSITION:
|
|
case WM_IME_SETCONTEXT:
|
|
case WM_IME_CONTROL:
|
|
case WM_IME_COMPOSITIONFULL:
|
|
case WM_IME_SELECT:
|
|
case WM_IME_CHAR:
|
|
case 0x287:
|
|
case WM_IME_KEYDOWN:
|
|
case WM_IME_KEYUP:
|
|
return FALSE;
|
|
case WM_SHOWWINDOW:
|
|
return !check_WM_SHOWWINDOW;
|
|
default:
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
static LRESULT CALLBACK ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
|
|
{
|
|
struct ime_call call =
|
|
{
|
|
.hkl = GetKeyboardLayout( 0 ), .himc = (HIMC)GetWindowLongPtrW( hwnd, IMMGWL_IMC ),
|
|
.func = MSG_IME_UI, .message = {.msg = msg, .wparam = wparam, .lparam = lparam}
|
|
};
|
|
LONG_PTR ptr;
|
|
|
|
ime_trace( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam );
|
|
|
|
if (ignore_message( msg, wparam )) return DefWindowProcW( hwnd, msg, wparam, lparam );
|
|
|
|
ptr = GetWindowLongPtrW( hwnd, IMMGWL_PRIVATE );
|
|
ok( !ptr, "got IMMGWL_PRIVATE %#Ix\n", ptr );
|
|
|
|
ime_calls[ime_call_count++] = call;
|
|
return DefWindowProcW( hwnd, msg, wparam, lparam );
|
|
}
|
|
|
|
static LRESULT CALLBACK test_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
|
|
{
|
|
struct ime_call call =
|
|
{
|
|
.hkl = GetKeyboardLayout( 0 ), .himc = ImmGetContext( hwnd ),
|
|
.func = MSG_TEST_WIN, .message = {.msg = msg, .wparam = wparam, .lparam = lparam}
|
|
};
|
|
|
|
ime_trace( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam );
|
|
|
|
if (ignore_message( msg, wparam )) return DefWindowProcW( hwnd, msg, wparam, lparam );
|
|
|
|
if (msg == WM_IME_COMPOSITION)
|
|
{
|
|
ImmGetCompositionStringW( call.himc, GCS_COMPSTR, call.comp, sizeof(call.comp) );
|
|
ImmGetCompositionStringW( call.himc, GCS_RESULTSTR, call.result, sizeof(call.result) );
|
|
}
|
|
|
|
ime_calls[ime_call_count++] = call;
|
|
return DefWindowProcW( hwnd, msg, wparam, lparam );
|
|
}
|
|
|
|
static WNDCLASSEXW ime_ui_class =
|
|
{
|
|
.cbSize = sizeof(WNDCLASSEXW),
|
|
.style = CS_IME,
|
|
.lpfnWndProc = ime_ui_window_proc,
|
|
.cbWndExtra = 2 * sizeof(LONG_PTR),
|
|
.lpszClassName = L"WineTestIME",
|
|
};
|
|
|
|
static WNDCLASSEXW test_class =
|
|
{
|
|
.cbSize = sizeof(WNDCLASSEXW),
|
|
.lpfnWndProc = test_window_proc,
|
|
.lpszClassName = L"WineTest",
|
|
};
|
|
|
|
/*
|
|
* msgspy - record and analyse message traces sent to a certain window
|
|
*/
|
|
typedef struct _msgs {
|
|
CWPSTRUCT msg;
|
|
BOOL post;
|
|
} imm_msgs;
|
|
|
|
static struct _msg_spy {
|
|
HWND hwnd;
|
|
HHOOK get_msg_hook;
|
|
HHOOK call_wnd_proc_hook;
|
|
imm_msgs msgs[64];
|
|
unsigned int i_msg;
|
|
} msg_spy;
|
|
|
|
typedef struct
|
|
{
|
|
DWORD type;
|
|
union
|
|
{
|
|
MOUSEINPUT mi;
|
|
KEYBDINPUT ki;
|
|
HARDWAREINPUT hi;
|
|
} u;
|
|
} TEST_INPUT;
|
|
|
|
static UINT (WINAPI *pSendInput) (UINT, INPUT*, size_t);
|
|
|
|
static LRESULT CALLBACK get_msg_filter(int nCode, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
if (HC_ACTION == nCode) {
|
|
MSG *msg = (MSG*)lParam;
|
|
|
|
if ((msg->hwnd == msg_spy.hwnd || msg_spy.hwnd == NULL) &&
|
|
(msg_spy.i_msg < ARRAY_SIZE(msg_spy.msgs)))
|
|
{
|
|
msg_spy.msgs[msg_spy.i_msg].msg.hwnd = msg->hwnd;
|
|
msg_spy.msgs[msg_spy.i_msg].msg.message = msg->message;
|
|
msg_spy.msgs[msg_spy.i_msg].msg.wParam = msg->wParam;
|
|
msg_spy.msgs[msg_spy.i_msg].msg.lParam = msg->lParam;
|
|
msg_spy.msgs[msg_spy.i_msg].post = TRUE;
|
|
msg_spy.i_msg++;
|
|
}
|
|
}
|
|
|
|
return CallNextHookEx(msg_spy.get_msg_hook, nCode, wParam, lParam);
|
|
}
|
|
|
|
static LRESULT CALLBACK call_wnd_proc_filter(int nCode, WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
if (HC_ACTION == nCode) {
|
|
CWPSTRUCT *cwp = (CWPSTRUCT*)lParam;
|
|
|
|
if (((cwp->hwnd == msg_spy.hwnd || msg_spy.hwnd == NULL)) &&
|
|
(msg_spy.i_msg < ARRAY_SIZE(msg_spy.msgs)))
|
|
{
|
|
memcpy(&msg_spy.msgs[msg_spy.i_msg].msg, cwp, sizeof(msg_spy.msgs[0].msg));
|
|
msg_spy.msgs[msg_spy.i_msg].post = FALSE;
|
|
msg_spy.i_msg++;
|
|
}
|
|
}
|
|
|
|
return CallNextHookEx(msg_spy.call_wnd_proc_hook, nCode, wParam, lParam);
|
|
}
|
|
|
|
static void msg_spy_pump_msg_queue(void) {
|
|
MSG msg;
|
|
|
|
while(PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessageW(&msg);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void msg_spy_flush_msgs(void) {
|
|
msg_spy_pump_msg_queue();
|
|
msg_spy.i_msg = 0;
|
|
}
|
|
|
|
static imm_msgs* msg_spy_find_next_msg(UINT message, UINT *start) {
|
|
UINT i;
|
|
|
|
msg_spy_pump_msg_queue();
|
|
|
|
if (msg_spy.i_msg >= ARRAY_SIZE(msg_spy.msgs))
|
|
fprintf(stdout, "%s:%d: msg_spy: message buffer overflow!\n",
|
|
__FILE__, __LINE__);
|
|
|
|
for (i = *start; i < msg_spy.i_msg; i++)
|
|
if (msg_spy.msgs[i].msg.message == message)
|
|
{
|
|
*start = i+1;
|
|
return &msg_spy.msgs[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static imm_msgs* msg_spy_find_msg(UINT message) {
|
|
UINT i = 0;
|
|
|
|
return msg_spy_find_next_msg(message, &i);
|
|
}
|
|
|
|
static void msg_spy_init(HWND hwnd) {
|
|
msg_spy.hwnd = hwnd;
|
|
msg_spy.get_msg_hook =
|
|
SetWindowsHookExW(WH_GETMESSAGE, get_msg_filter, GetModuleHandleW(NULL),
|
|
GetCurrentThreadId());
|
|
msg_spy.call_wnd_proc_hook =
|
|
SetWindowsHookExW(WH_CALLWNDPROC, call_wnd_proc_filter,
|
|
GetModuleHandleW(NULL), GetCurrentThreadId());
|
|
msg_spy.i_msg = 0;
|
|
|
|
msg_spy_flush_msgs();
|
|
}
|
|
|
|
static void msg_spy_cleanup(void) {
|
|
if (msg_spy.get_msg_hook)
|
|
UnhookWindowsHookEx(msg_spy.get_msg_hook);
|
|
if (msg_spy.call_wnd_proc_hook)
|
|
UnhookWindowsHookEx(msg_spy.call_wnd_proc_hook);
|
|
memset(&msg_spy, 0, sizeof(msg_spy));
|
|
}
|
|
|
|
/*
|
|
* imm32 test cases - Issue some IMM commands on a dummy window and analyse the
|
|
* messages being sent to this window in response.
|
|
*/
|
|
static const char wndcls[] = "winetest_imm32_wndcls";
|
|
static enum { PHASE_UNKNOWN, FIRST_WINDOW, SECOND_WINDOW,
|
|
CREATE_CANCEL, NCCREATE_CANCEL, IME_DISABLED } test_phase;
|
|
static HWND hwnd, child;
|
|
|
|
static HWND get_ime_window(void);
|
|
|
|
static void load_resource( const WCHAR *name, WCHAR *filename )
|
|
{
|
|
static WCHAR path[MAX_PATH];
|
|
DWORD written;
|
|
HANDLE file;
|
|
HRSRC res;
|
|
void *ptr;
|
|
|
|
GetTempPathW( ARRAY_SIZE(path), path );
|
|
GetTempFileNameW( path, name, 0, filename );
|
|
|
|
file = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0 );
|
|
ok( file != INVALID_HANDLE_VALUE, "failed to create %s, error %lu\n", debugstr_w(filename), GetLastError() );
|
|
|
|
res = FindResourceW( NULL, name, L"TESTDLL" );
|
|
ok( res != 0, "couldn't find resource\n" );
|
|
ptr = LockResource( LoadResource( GetModuleHandleW( NULL ), res ) );
|
|
WriteFile( file, ptr, SizeofResource( GetModuleHandleW( NULL ), res ), &written, NULL );
|
|
ok( written == SizeofResource( GetModuleHandleW( NULL ), res ), "couldn't write resource\n" );
|
|
CloseHandle( file );
|
|
}
|
|
|
|
static LRESULT WINAPI wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HWND default_ime_wnd;
|
|
switch (msg)
|
|
{
|
|
case WM_IME_SETCONTEXT:
|
|
if (wParam) CHECK_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
|
|
else CHECK_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
|
|
ok(lParam == ISC_SHOWUIALL || !lParam, "lParam = %Ix\n", lParam);
|
|
return TRUE;
|
|
case WM_NCCREATE:
|
|
default_ime_wnd = get_ime_window();
|
|
switch(test_phase) {
|
|
case FIRST_WINDOW:
|
|
case IME_DISABLED:
|
|
ok(!default_ime_wnd, "expected no IME windows\n");
|
|
break;
|
|
case SECOND_WINDOW:
|
|
ok(default_ime_wnd != NULL, "expected IME window existence\n");
|
|
break;
|
|
default:
|
|
break; /* do nothing */
|
|
}
|
|
if (test_phase == NCCREATE_CANCEL)
|
|
return FALSE;
|
|
return TRUE;
|
|
case WM_NCCALCSIZE:
|
|
default_ime_wnd = get_ime_window();
|
|
switch(test_phase) {
|
|
case FIRST_WINDOW:
|
|
case SECOND_WINDOW:
|
|
case CREATE_CANCEL:
|
|
ok(default_ime_wnd != NULL, "expected IME window existence\n");
|
|
break;
|
|
case IME_DISABLED:
|
|
ok(!default_ime_wnd, "expected no IME windows\n");
|
|
break;
|
|
default:
|
|
break; /* do nothing */
|
|
}
|
|
break;
|
|
case WM_CREATE:
|
|
default_ime_wnd = get_ime_window();
|
|
switch(test_phase) {
|
|
case FIRST_WINDOW:
|
|
case SECOND_WINDOW:
|
|
case CREATE_CANCEL:
|
|
ok(default_ime_wnd != NULL, "expected IME window existence\n");
|
|
break;
|
|
case IME_DISABLED:
|
|
ok(!default_ime_wnd, "expected no IME windows\n");
|
|
break;
|
|
default:
|
|
break; /* do nothing */
|
|
}
|
|
if (test_phase == CREATE_CANCEL)
|
|
return -1;
|
|
return TRUE;
|
|
}
|
|
|
|
return DefWindowProcA(hWnd,msg,wParam,lParam);
|
|
}
|
|
|
|
static BOOL is_ime_enabled(void)
|
|
{
|
|
HIMC himc;
|
|
HWND wnd;
|
|
|
|
wnd = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
ok(wnd != NULL, "CreateWindow failed\n");
|
|
|
|
himc = ImmGetContext(wnd);
|
|
if (!himc)
|
|
{
|
|
DestroyWindow(wnd);
|
|
return FALSE;
|
|
}
|
|
|
|
ImmReleaseContext(wnd, himc);
|
|
DestroyWindow(wnd);
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL init(void) {
|
|
WNDCLASSEXA wc;
|
|
HMODULE hmod,huser;
|
|
|
|
hmod = GetModuleHandleA("imm32.dll");
|
|
huser = GetModuleHandleA("user32");
|
|
pImmAssociateContextEx = (void*)GetProcAddress(hmod, "ImmAssociateContextEx");
|
|
pImmIsUIMessageA = (void*)GetProcAddress(hmod, "ImmIsUIMessageA");
|
|
pSendInput = (void*)GetProcAddress(huser, "SendInput");
|
|
pNtUserAssociateInputContext = (void*)GetProcAddress(GetModuleHandleW(L"win32u.dll"),
|
|
"NtUserAssociateInputContext");
|
|
|
|
wc.cbSize = sizeof(WNDCLASSEXA);
|
|
wc.style = 0;
|
|
wc.lpfnWndProc = wndProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = GetModuleHandleA(NULL);
|
|
wc.hIcon = LoadIconA(NULL, (LPCSTR)IDI_APPLICATION);
|
|
wc.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW);
|
|
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = wndcls;
|
|
wc.hIconSm = LoadIconA(NULL, (LPCSTR)IDI_APPLICATION);
|
|
|
|
if (!RegisterClassExA(&wc))
|
|
return FALSE;
|
|
|
|
hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
|
|
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
|
|
240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
|
|
if (!hwnd)
|
|
return FALSE;
|
|
|
|
child = CreateWindowA("edit", "edit", WS_CHILD | WS_VISIBLE, 0, 0, 50, 50, hwnd, 0, 0, 0);
|
|
if (!child)
|
|
return FALSE;
|
|
|
|
ShowWindow(hwnd, SW_SHOWNORMAL);
|
|
UpdateWindow(hwnd);
|
|
|
|
msg_spy_init(hwnd);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void cleanup(void) {
|
|
msg_spy_cleanup();
|
|
if (hwnd)
|
|
DestroyWindow(hwnd);
|
|
UnregisterClassA(wndcls, GetModuleHandleW(NULL));
|
|
}
|
|
|
|
static void test_ImmNotifyIME(void) {
|
|
static const char string[] = "wine";
|
|
char resstr[16] = "";
|
|
HIMC imc;
|
|
BOOL ret;
|
|
|
|
imc = ImmGetContext(hwnd);
|
|
msg_spy_flush_msgs();
|
|
|
|
ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
|
|
ok(broken(!ret) ||
|
|
ret, /* Vista+ */
|
|
"Canceling an empty composition string should succeed.\n");
|
|
ok(!msg_spy_find_msg(WM_IME_COMPOSITION), "Windows does not post "
|
|
"WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
|
|
"the composition string being canceled is empty.\n");
|
|
|
|
ImmSetCompositionStringA(imc, SCS_SETSTR, string, sizeof(string), NULL, 0);
|
|
msg_spy_flush_msgs();
|
|
|
|
ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
|
|
msg_spy_flush_msgs();
|
|
|
|
/* behavior differs between win9x and NT */
|
|
ret = ImmGetCompositionStringA(imc, GCS_COMPSTR, resstr, sizeof(resstr));
|
|
ok(!ret, "After being cancelled the composition string is empty.\n");
|
|
|
|
msg_spy_flush_msgs();
|
|
|
|
ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
|
|
ok(broken(!ret) ||
|
|
ret, /* Vista+ */
|
|
"Canceling an empty composition string should succeed.\n");
|
|
ok(!msg_spy_find_msg(WM_IME_COMPOSITION), "Windows does not post "
|
|
"WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
|
|
"the composition string being canceled is empty.\n");
|
|
|
|
msg_spy_flush_msgs();
|
|
ImmReleaseContext(hwnd, imc);
|
|
|
|
imc = ImmCreateContext();
|
|
ImmDestroyContext(imc);
|
|
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmNotifyIME((HIMC)0xdeadcafe, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
|
|
ok (ret == 0, "Bad IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmNotifyIME(0x00000000, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
|
|
ok (ret == 0, "NULL IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_SUCCESS, "wrong last error %08x!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
|
|
ok (ret == 0, "Destroyed IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
|
|
|
|
}
|
|
|
|
static struct {
|
|
WNDPROC old_wnd_proc;
|
|
BOOL catch_result_str;
|
|
BOOL catch_ime_char;
|
|
DWORD start;
|
|
DWORD timer_id;
|
|
} ime_composition_test;
|
|
|
|
static LRESULT WINAPI test_ime_wnd_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (msg)
|
|
{
|
|
case WM_IME_COMPOSITION:
|
|
if ((lParam & GCS_RESULTSTR) && !ime_composition_test.catch_result_str) {
|
|
HWND hwndIme;
|
|
WCHAR wstring[20];
|
|
HIMC imc;
|
|
LONG size;
|
|
LRESULT ret;
|
|
|
|
hwndIme = ImmGetDefaultIMEWnd(hWnd);
|
|
ok(hwndIme != NULL, "expected IME window existence\n");
|
|
|
|
ok(!ime_composition_test.catch_ime_char, "WM_IME_CHAR is sent\n");
|
|
ret = CallWindowProcA(ime_composition_test.old_wnd_proc,
|
|
hWnd, msg, wParam, lParam);
|
|
ok(ime_composition_test.catch_ime_char, "WM_IME_CHAR isn't sent\n");
|
|
|
|
ime_composition_test.catch_ime_char = FALSE;
|
|
SendMessageA(hwndIme, msg, wParam, lParam);
|
|
ok(!ime_composition_test.catch_ime_char, "WM_IME_CHAR is sent\n");
|
|
|
|
imc = ImmGetContext(hWnd);
|
|
size = ImmGetCompositionStringW(imc, GCS_RESULTSTR,
|
|
wstring, sizeof(wstring));
|
|
ok(size > 0, "ImmGetCompositionString(GCS_RESULTSTR) is %ld\n", size);
|
|
ImmReleaseContext(hwnd, imc);
|
|
|
|
ime_composition_test.catch_result_str = TRUE;
|
|
return ret;
|
|
}
|
|
break;
|
|
case WM_IME_CHAR:
|
|
if (!ime_composition_test.catch_result_str)
|
|
ime_composition_test.catch_ime_char = TRUE;
|
|
break;
|
|
case WM_TIMER:
|
|
if (wParam == ime_composition_test.timer_id) {
|
|
HWND parent = GetParent(hWnd);
|
|
char title[64];
|
|
int left = 20 - (GetTickCount() - ime_composition_test.start) / 1000;
|
|
wsprintfA(title, "%sLeft %d sec. - IME composition test",
|
|
ime_composition_test.catch_result_str ? "[*] " : "", left);
|
|
SetWindowTextA(parent, title);
|
|
if (left <= 0)
|
|
DestroyWindow(parent);
|
|
else
|
|
SetTimer(hWnd, wParam, 100, NULL);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
return CallWindowProcA(ime_composition_test.old_wnd_proc,
|
|
hWnd, msg, wParam, lParam);
|
|
}
|
|
|
|
static void test_SCS_SETSTR(void)
|
|
{
|
|
HIMC imc;
|
|
static const WCHAR string[] = {'w','i','n','e',0x65e5,0x672c,0x8a9e};
|
|
char cstring[20];
|
|
WCHAR wstring[20];
|
|
LONG len;
|
|
LONG alen,wlen;
|
|
BOOL ret;
|
|
DWORD prop;
|
|
|
|
imc = ImmGetContext(hwnd);
|
|
ret = ImmSetCompositionStringW(imc, SCS_SETSTR, string, sizeof(string), NULL, 0);
|
|
if (!ret) {
|
|
win_skip("Composition isn't supported\n");
|
|
ImmReleaseContext(hwnd, imc);
|
|
return;
|
|
}
|
|
|
|
ret = ImmSetCompositionStringW(imc, SCS_SETSTR, NULL, 128, NULL, 128);
|
|
ok(ret, "got error %lu.\n", GetLastError());
|
|
|
|
alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20);
|
|
ok(!alen, "got %ld.\n", alen);
|
|
wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 20);
|
|
ok(!wlen, "got %ld.\n", alen);
|
|
|
|
ret = ImmSetCompositionStringW(imc, SCS_SETSTR, string, sizeof(string), NULL, 2);
|
|
ok(ret, "got error %lu.\n", GetLastError());
|
|
|
|
msg_spy_flush_msgs();
|
|
|
|
alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20);
|
|
wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 20);
|
|
/* windows machines without any IME installed just return 0 above */
|
|
if( alen && wlen)
|
|
{
|
|
len = ImmGetCompositionStringW(imc, GCS_COMPATTR, NULL, 0);
|
|
ok(len*sizeof(WCHAR)==wlen,"GCS_COMPATTR(W) not returning correct count\n");
|
|
len = ImmGetCompositionStringA(imc, GCS_COMPATTR, NULL, 0);
|
|
ok(len==alen,"GCS_COMPATTR(A) not returning correct count\n");
|
|
|
|
/* Get strings with exactly matching buffer sizes. */
|
|
memset(wstring, 0x1a, sizeof(wstring));
|
|
memset(cstring, 0x1a, sizeof(cstring));
|
|
|
|
len = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, alen);
|
|
ok(len == alen, "Unexpected length %ld.\n", len);
|
|
ok(cstring[alen] == 0x1a, "Unexpected buffer contents.\n");
|
|
|
|
len = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, wlen);
|
|
ok(len == wlen, "Unexpected length %ld.\n", len);
|
|
ok(wstring[wlen/sizeof(WCHAR)] == 0x1a1a, "Unexpected buffer contents.\n");
|
|
|
|
/* Get strings with exactly smaller buffer sizes. */
|
|
memset(wstring, 0x1a, sizeof(wstring));
|
|
memset(cstring, 0x1a, sizeof(cstring));
|
|
|
|
/* Returns 0 but still fills buffer. */
|
|
len = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, alen - 1);
|
|
ok(!len, "Unexpected length %ld.\n", len);
|
|
ok(cstring[0] == 'w', "Unexpected buffer contents %s.\n", cstring);
|
|
|
|
len = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, wlen - 1);
|
|
ok(len == wlen - 1, "Unexpected length %ld.\n", len);
|
|
ok(!memcmp(wstring, string, wlen - 1), "Unexpected buffer contents.\n");
|
|
|
|
/* Get the size of the required output buffer. */
|
|
memset(wstring, 0x1a, sizeof(wstring));
|
|
memset(cstring, 0x1a, sizeof(cstring));
|
|
|
|
len = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 0);
|
|
ok(len == alen, "Unexpected length %ld.\n", len);
|
|
ok(cstring[0] == 0x1a, "Unexpected buffer contents %s.\n", cstring);
|
|
|
|
len = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 0);
|
|
ok(len == wlen, "Unexpected length %ld.\n", len);
|
|
ok(wstring[0] == 0x1a1a, "Unexpected buffer contents.\n");
|
|
}
|
|
else
|
|
win_skip("Composition string isn't available\n");
|
|
|
|
ImmReleaseContext(hwnd, imc);
|
|
|
|
/* Test composition results input by IMM API */
|
|
prop = ImmGetProperty(GetKeyboardLayout(0), IGP_SETCOMPSTR);
|
|
if (!(prop & SCS_CAP_COMPSTR)) {
|
|
/* Wine's IME doesn't support SCS_SETSTR in ImmSetCompositionString */
|
|
skip("This IME doesn't support SCS_SETSTR\n");
|
|
}
|
|
else {
|
|
ime_composition_test.old_wnd_proc =
|
|
(WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
|
|
(LONG_PTR)test_ime_wnd_proc);
|
|
imc = ImmGetContext(hwnd);
|
|
msg_spy_flush_msgs();
|
|
|
|
ret = ImmSetCompositionStringW(imc, SCS_SETSTR,
|
|
string, sizeof(string), NULL,0);
|
|
ok(ret, "ImmSetCompositionStringW failed\n");
|
|
wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR,
|
|
wstring, sizeof(wstring));
|
|
if (wlen > 0) {
|
|
ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
|
|
ok(ret, "ImmNotifyIME(CPS_COMPLETE) failed\n");
|
|
msg_spy_flush_msgs();
|
|
ok(ime_composition_test.catch_result_str,
|
|
"WM_IME_COMPOSITION(GCS_RESULTSTR) isn't sent\n");
|
|
}
|
|
else
|
|
win_skip("Composition string isn't available\n");
|
|
ImmReleaseContext(hwnd, imc);
|
|
SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
|
|
(LONG_PTR)ime_composition_test.old_wnd_proc);
|
|
msg_spy_flush_msgs();
|
|
}
|
|
|
|
/* Test composition results input by hand */
|
|
memset(&ime_composition_test, 0, sizeof(ime_composition_test));
|
|
if (winetest_interactive) {
|
|
HWND hwndMain, hwndChild;
|
|
MSG msg;
|
|
const DWORD MY_TIMER = 0xcaffe;
|
|
|
|
hwndMain = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls,
|
|
"IME composition test",
|
|
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
CW_USEDEFAULT, CW_USEDEFAULT, 320, 160,
|
|
NULL, NULL, GetModuleHandleA(NULL), NULL);
|
|
hwndChild = CreateWindowExA(0, "static",
|
|
"Input a DBCS character here using IME.",
|
|
WS_CHILD | WS_VISIBLE,
|
|
0, 0, 320, 100, hwndMain, NULL,
|
|
GetModuleHandleA(NULL), NULL);
|
|
|
|
ime_composition_test.old_wnd_proc =
|
|
(WNDPROC)SetWindowLongPtrA(hwndChild, GWLP_WNDPROC,
|
|
(LONG_PTR)test_ime_wnd_proc);
|
|
|
|
SetFocus(hwndChild);
|
|
|
|
ime_composition_test.timer_id = MY_TIMER;
|
|
ime_composition_test.start = GetTickCount();
|
|
SetTimer(hwndChild, ime_composition_test.timer_id, 100, NULL);
|
|
while (GetMessageA(&msg, NULL, 0, 0)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessageA(&msg);
|
|
if (!IsWindow(hwndMain))
|
|
break;
|
|
}
|
|
if (!ime_composition_test.catch_result_str)
|
|
skip("WM_IME_COMPOSITION(GCS_RESULTSTR) isn't tested\n");
|
|
msg_spy_flush_msgs();
|
|
}
|
|
|
|
SetLastError(0xdeadbeef);
|
|
imc = ImmGetContext(hwnd);
|
|
ok(imc != 0, "ImmGetContext() failed. Last error: %lu\n", GetLastError());
|
|
if (!imc)
|
|
return;
|
|
|
|
ret = ImmSetCompositionStringW(imc, SCS_SETSTR, NULL, 0, NULL, 0);
|
|
ok(broken(!ret) ||
|
|
ret, /* Vista+ */
|
|
"ImmSetCompositionStringW() failed.\n");
|
|
|
|
ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGEATTR,
|
|
NULL, 0, NULL, 0);
|
|
ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
|
|
|
|
ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGECLAUSE,
|
|
NULL, 0, NULL, 0);
|
|
ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
|
|
|
|
ret = ImmSetCompositionStringW(imc, SCS_CHANGEATTR | SCS_CHANGECLAUSE,
|
|
NULL, 0, NULL, 0);
|
|
ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
|
|
|
|
ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGEATTR | SCS_CHANGECLAUSE,
|
|
NULL, 0, NULL, 0);
|
|
ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
|
|
|
|
ImmReleaseContext(hwnd, imc);
|
|
}
|
|
|
|
static void test_ImmIME(void)
|
|
{
|
|
HIMC imc;
|
|
|
|
imc = ImmGetContext(hwnd);
|
|
if (imc)
|
|
{
|
|
BOOL rc;
|
|
rc = ImmConfigureIMEA(imc, NULL, IME_CONFIG_REGISTERWORD, NULL);
|
|
ok (rc == 0, "ImmConfigureIMEA did not fail\n");
|
|
rc = ImmConfigureIMEW(imc, NULL, IME_CONFIG_REGISTERWORD, NULL);
|
|
ok (rc == 0, "ImmConfigureIMEW did not fail\n");
|
|
}
|
|
ImmReleaseContext(hwnd,imc);
|
|
}
|
|
|
|
static void test_ImmAssociateContextEx(void)
|
|
{
|
|
HIMC imc;
|
|
BOOL rc;
|
|
|
|
if (!pImmAssociateContextEx) return;
|
|
|
|
imc = ImmGetContext(hwnd);
|
|
if (imc)
|
|
{
|
|
HIMC retimc, newimc;
|
|
HWND focus;
|
|
|
|
SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, TRUE);
|
|
SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, TRUE);
|
|
|
|
ok(GetActiveWindow() == hwnd, "hwnd is not active\n");
|
|
newimc = ImmCreateContext();
|
|
ok(newimc != imc, "handles should not be the same\n");
|
|
rc = pImmAssociateContextEx(NULL, NULL, 0);
|
|
ok(!rc, "ImmAssociateContextEx succeeded\n");
|
|
SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
|
|
SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
|
|
rc = pImmAssociateContextEx(hwnd, NULL, 0);
|
|
CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
|
|
CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
|
|
ok(rc, "ImmAssociateContextEx failed\n");
|
|
rc = pImmAssociateContextEx(NULL, imc, 0);
|
|
ok(!rc, "ImmAssociateContextEx succeeded\n");
|
|
|
|
SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
|
|
SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
|
|
rc = pImmAssociateContextEx(hwnd, imc, 0);
|
|
CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
|
|
CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
|
|
ok(rc, "ImmAssociateContextEx failed\n");
|
|
retimc = ImmGetContext(hwnd);
|
|
ok(retimc == imc, "handles should be the same\n");
|
|
ImmReleaseContext(hwnd,retimc);
|
|
|
|
rc = pImmAssociateContextEx(hwnd, imc, 0);
|
|
ok(rc, "ImmAssociateContextEx failed\n");
|
|
|
|
SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
|
|
SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
|
|
rc = pImmAssociateContextEx(hwnd, newimc, 0);
|
|
CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
|
|
CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
|
|
ok(rc, "ImmAssociateContextEx failed\n");
|
|
retimc = ImmGetContext(hwnd);
|
|
ok(retimc == newimc, "handles should be the same\n");
|
|
ImmReleaseContext(hwnd,retimc);
|
|
|
|
focus = CreateWindowA("button", "button", 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
ok(focus != NULL, "CreateWindow failed\n");
|
|
SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
|
|
SetFocus(focus);
|
|
CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
|
|
rc = pImmAssociateContextEx(hwnd, imc, 0);
|
|
ok(rc, "ImmAssociateContextEx failed\n");
|
|
SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
|
|
DestroyWindow(focus);
|
|
CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
|
|
|
|
SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
|
|
SetFocus(child);
|
|
CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
|
|
rc = pImmAssociateContextEx(hwnd, newimc, 0);
|
|
ok(rc, "ImmAssociateContextEx failed\n");
|
|
SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
|
|
SetFocus(hwnd);
|
|
CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
|
|
|
|
SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
|
|
SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
|
|
rc = pImmAssociateContextEx(hwnd, NULL, IACE_DEFAULT);
|
|
CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
|
|
CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
|
|
ok(rc, "ImmAssociateContextEx failed\n");
|
|
|
|
SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, FALSE);
|
|
SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, FALSE);
|
|
}
|
|
ImmReleaseContext(hwnd,imc);
|
|
}
|
|
|
|
/* similar to above, but using NtUserAssociateInputContext */
|
|
static void test_NtUserAssociateInputContext(void)
|
|
{
|
|
HIMC imc;
|
|
UINT rc;
|
|
|
|
if (!pNtUserAssociateInputContext)
|
|
{
|
|
win_skip("NtUserAssociateInputContext not available\n");
|
|
return;
|
|
}
|
|
|
|
imc = ImmGetContext(hwnd);
|
|
if (imc)
|
|
{
|
|
HIMC retimc, newimc;
|
|
HWND focus;
|
|
|
|
SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, TRUE);
|
|
SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, TRUE);
|
|
|
|
ok(GetActiveWindow() == hwnd, "hwnd is not active\n");
|
|
newimc = ImmCreateContext();
|
|
ok(newimc != imc, "handles should not be the same\n");
|
|
rc = pNtUserAssociateInputContext(NULL, NULL, 0);
|
|
ok(rc == 2, "NtUserAssociateInputContext returned %x\n", rc);
|
|
rc = pNtUserAssociateInputContext(hwnd, NULL, 0);
|
|
ok(rc == 1, "NtUserAssociateInputContext returned %x\n", rc);
|
|
rc = pNtUserAssociateInputContext(NULL, imc, 0);
|
|
ok(rc == 2, "NtUserAssociateInputContext returned %x\n", rc);
|
|
|
|
rc = pNtUserAssociateInputContext(hwnd, imc, 0);
|
|
ok(rc == 1, "NtUserAssociateInputContext returned %x\n", rc);
|
|
retimc = ImmGetContext(hwnd);
|
|
ok(retimc == imc, "handles should be the same\n");
|
|
ImmReleaseContext(hwnd,retimc);
|
|
|
|
rc = pNtUserAssociateInputContext(hwnd, imc, 0);
|
|
ok(rc == 0, "NtUserAssociateInputContext returned %x\n", rc);
|
|
|
|
rc = pNtUserAssociateInputContext(hwnd, newimc, 0);
|
|
ok(rc == 1, "NtUserAssociateInputContext returned %x\n", rc);
|
|
retimc = ImmGetContext(hwnd);
|
|
ok(retimc == newimc, "handles should be the same\n");
|
|
ImmReleaseContext(hwnd,retimc);
|
|
|
|
focus = CreateWindowA("button", "button", 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
ok(focus != NULL, "CreateWindow failed\n");
|
|
SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
|
|
SetFocus(focus);
|
|
CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
|
|
rc = pNtUserAssociateInputContext(hwnd, imc, 0);
|
|
ok(rc == 0, "NtUserAssociateInputContext returned %x\n", rc);
|
|
SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
|
|
DestroyWindow(focus);
|
|
CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
|
|
|
|
SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
|
|
SetFocus(child);
|
|
CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
|
|
rc = pNtUserAssociateInputContext(hwnd, newimc, 0);
|
|
ok(rc == 0, "NtUserAssociateInputContext returned %x\n", rc);
|
|
SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE);
|
|
SetFocus(hwnd);
|
|
CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE);
|
|
|
|
rc = pNtUserAssociateInputContext(hwnd, NULL, IACE_DEFAULT);
|
|
ok(rc == 1, "NtUserAssociateInputContext returned %x\n", rc);
|
|
|
|
SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, FALSE);
|
|
SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, FALSE);
|
|
}
|
|
ImmReleaseContext(hwnd,imc);
|
|
}
|
|
|
|
struct test_cross_thread_himc_params
|
|
{
|
|
HWND hwnd;
|
|
HANDLE event;
|
|
HIMC himc[2];
|
|
INPUTCONTEXT *contexts[2];
|
|
};
|
|
|
|
static DWORD WINAPI test_cross_thread_himc_thread( void *arg )
|
|
{
|
|
CANDIDATEFORM candidate = {.dwIndex = 1, .dwStyle = CFS_CANDIDATEPOS};
|
|
struct test_cross_thread_himc_params *params = arg;
|
|
COMPOSITIONFORM composition = {0};
|
|
INPUTCONTEXT *contexts[2];
|
|
HIMC himc[2], tmp_himc;
|
|
LOGFONTW fontW = {0};
|
|
HWND hwnd, tmp_hwnd;
|
|
POINT pos = {0};
|
|
MSG msg;
|
|
|
|
hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok_ne( NULL, hwnd, HWND, "%p" );
|
|
himc[0] = ImmGetContext( hwnd );
|
|
ok_ne( NULL, himc[0], HIMC, "%p" );
|
|
contexts[0] = ImmLockIMC( himc[0] );
|
|
ok_ne( NULL, contexts[0], INPUTCONTEXT *, "%p" );
|
|
contexts[0]->hWnd = hwnd;
|
|
|
|
tmp_hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
tmp_himc = ImmGetContext( tmp_hwnd );
|
|
ok_eq( himc[0], tmp_himc, HIMC, "%p" );
|
|
ok_ret( 1, ImmReleaseContext( tmp_hwnd, tmp_himc ) );
|
|
ok_ret( 1, DestroyWindow( tmp_hwnd ) );
|
|
|
|
himc[1] = ImmCreateContext();
|
|
ok_ne( NULL, himc[1], HIMC, "%p" );
|
|
contexts[1] = ImmLockIMC( himc[1] );
|
|
ok_ne( NULL, contexts[1], INPUTCONTEXT *, "%p" );
|
|
contexts[1]->hWnd = hwnd;
|
|
|
|
ok_ret( 1, ImmSetOpenStatus( himc[0], 0xdeadbeef ) );
|
|
ok_ret( 1, ImmSetOpenStatus( himc[1], 0xfeedcafe ) );
|
|
ok_ret( 1, ImmSetCompositionWindow( himc[0], &composition ) );
|
|
ok_ret( 1, ImmSetCompositionWindow( himc[1], &composition ) );
|
|
ok_ret( 1, ImmSetCandidateWindow( himc[0], &candidate ) );
|
|
ok_ret( 1, ImmSetCandidateWindow( himc[1], &candidate ) );
|
|
ok_ret( 1, ImmSetStatusWindowPos( himc[0], &pos ) );
|
|
ok_ret( 1, ImmSetStatusWindowPos( himc[1], &pos ) );
|
|
ok_ret( 1, ImmSetCompositionFontW( himc[0], &fontW ) );
|
|
ok_ret( 1, ImmSetCompositionFontW( himc[1], &fontW ) );
|
|
|
|
params->hwnd = hwnd;
|
|
params->himc[0] = himc[0];
|
|
params->himc[1] = himc[1];
|
|
params->contexts[0] = contexts[0];
|
|
params->contexts[1] = contexts[1];
|
|
SetEvent( params->event );
|
|
|
|
while (GetMessageW( &msg, 0, 0, 0 ))
|
|
{
|
|
TranslateMessage( &msg );
|
|
DispatchMessageW( &msg );
|
|
}
|
|
|
|
ok_ret( 1, ImmUnlockIMC( himc[0] ) );
|
|
ok_ret( 1, ImmUnlockIMC( himc[1] ) );
|
|
|
|
ok_ret( 1, ImmDestroyContext( himc[1] ) );
|
|
ok_ret( 1, ImmReleaseContext( hwnd, himc[0] ) );
|
|
ok_ret( 0, DestroyWindow( hwnd ) );
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void test_cross_thread_himc(void)
|
|
{
|
|
static const WCHAR comp_string[] = L"CompString";
|
|
RECONVERTSTRING reconv = {.dwSize = sizeof(RECONVERTSTRING)};
|
|
struct test_cross_thread_himc_params params;
|
|
COMPOSITIONFORM composition = {0};
|
|
DWORD tid, conversion, sentence;
|
|
IMECHARPOSITION char_pos = {0};
|
|
CANDIDATEFORM candidate = {0};
|
|
COMPOSITIONSTRING *string;
|
|
HIMC himc[2], tmp_himc;
|
|
INPUTCONTEXT *tmp_ctx;
|
|
LOGFONTW fontW = {0};
|
|
LOGFONTA fontA = {0};
|
|
char buffer[512];
|
|
POINT pos = {0};
|
|
HANDLE thread;
|
|
BYTE *dst;
|
|
UINT ret;
|
|
|
|
himc[0] = ImmGetContext( hwnd );
|
|
ok_ne( NULL, himc[0], HIMC, "%p" );
|
|
ok_ne( NULL, ImmLockIMC( himc[0] ), INPUTCONTEXT *, "%p" );
|
|
ok_ret( 1, ImmUnlockIMC( himc[0] ) );
|
|
|
|
params.event = CreateEventW(NULL, TRUE, FALSE, NULL);
|
|
ok_ne( NULL, params.event, HANDLE, "%p" );
|
|
thread = CreateThread( NULL, 0, test_cross_thread_himc_thread, ¶ms, 0, &tid );
|
|
ok_ne( NULL, thread, HANDLE, "%p" );
|
|
WaitForSingleObject( params.event, INFINITE );
|
|
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
tmp_himc = ImmGetContext( params.hwnd );
|
|
ok_ne( himc[0], tmp_himc, HIMC, "%p" );
|
|
ok_eq( params.himc[0], tmp_himc, HIMC, "%p" );
|
|
ok_ret( 1, ImmReleaseContext( params.hwnd, tmp_himc ) );
|
|
|
|
himc[1] = ImmCreateContext();
|
|
ok_ne( NULL, himc[1], HIMC, "%p" );
|
|
tmp_ctx = ImmLockIMC( himc[1] );
|
|
ok_ne( NULL, tmp_ctx, INPUTCONTEXT *, "%p" );
|
|
|
|
tmp_ctx->hCompStr = ImmReSizeIMCC( tmp_ctx->hCompStr, 512 );
|
|
ok_ne( NULL, tmp_ctx->hCompStr, HIMCC, "%p" );
|
|
string = ImmLockIMCC( tmp_ctx->hCompStr );
|
|
ok_ne( NULL, string, COMPOSITIONSTRING *, "%p" );
|
|
string->dwSize = sizeof(COMPOSITIONSTRING);
|
|
string->dwCompStrLen = wcslen( comp_string );
|
|
string->dwCompStrOffset = string->dwSize;
|
|
dst = (BYTE *)string + string->dwCompStrOffset;
|
|
memcpy( dst, comp_string, string->dwCompStrLen * sizeof(WCHAR) );
|
|
string->dwSize += string->dwCompStrLen * sizeof(WCHAR);
|
|
|
|
string->dwCompClauseLen = 2 * sizeof(DWORD);
|
|
string->dwCompClauseOffset = string->dwSize;
|
|
dst = (BYTE *)string + string->dwCompClauseOffset;
|
|
*(DWORD *)(dst + 0 * sizeof(DWORD)) = 0;
|
|
*(DWORD *)(dst + 1 * sizeof(DWORD)) = string->dwCompStrLen;
|
|
string->dwSize += 2 * sizeof(DWORD);
|
|
|
|
string->dwCompAttrLen = string->dwCompStrLen;
|
|
string->dwCompAttrOffset = string->dwSize;
|
|
dst = (BYTE *)string + string->dwCompAttrOffset;
|
|
memset( dst, ATTR_INPUT, string->dwCompStrLen );
|
|
string->dwSize += string->dwCompStrLen;
|
|
ok_ret( 0, ImmUnlockIMCC( tmp_ctx->hCompStr ) );
|
|
|
|
ok_ret( 1, ImmUnlockIMC( himc[1] ) );
|
|
|
|
/* ImmLockIMC should succeed with cross thread HIMC */
|
|
|
|
tmp_ctx = ImmLockIMC( params.himc[0] );
|
|
ok_eq( params.contexts[0], tmp_ctx, INPUTCONTEXT *, "%p" );
|
|
ret = ImmGetIMCLockCount( params.himc[0] );
|
|
ok( ret >= 2, "got ret %u\n", ret );
|
|
|
|
tmp_ctx->hCompStr = ImmReSizeIMCC( tmp_ctx->hCompStr, 512 );
|
|
ok_ne( NULL, tmp_ctx->hCompStr, HIMCC, "%p" );
|
|
string = ImmLockIMCC( tmp_ctx->hCompStr );
|
|
ok_ne( NULL, string, COMPOSITIONSTRING *, "%p" );
|
|
string->dwSize = sizeof(COMPOSITIONSTRING);
|
|
string->dwCompStrLen = wcslen( comp_string );
|
|
string->dwCompStrOffset = string->dwSize;
|
|
dst = (BYTE *)string + string->dwCompStrOffset;
|
|
memcpy( dst, comp_string, string->dwCompStrLen * sizeof(WCHAR) );
|
|
string->dwSize += string->dwCompStrLen * sizeof(WCHAR);
|
|
|
|
string->dwCompClauseLen = 2 * sizeof(DWORD);
|
|
string->dwCompClauseOffset = string->dwSize;
|
|
dst = (BYTE *)string + string->dwCompClauseOffset;
|
|
*(DWORD *)(dst + 0 * sizeof(DWORD)) = 0;
|
|
*(DWORD *)(dst + 1 * sizeof(DWORD)) = string->dwCompStrLen;
|
|
string->dwSize += 2 * sizeof(DWORD);
|
|
|
|
string->dwCompAttrLen = string->dwCompStrLen;
|
|
string->dwCompAttrOffset = string->dwSize;
|
|
dst = (BYTE *)string + string->dwCompAttrOffset;
|
|
memset( dst, ATTR_INPUT, string->dwCompStrLen );
|
|
string->dwSize += string->dwCompStrLen;
|
|
ok_ret( 0, ImmUnlockIMCC( tmp_ctx->hCompStr ) );
|
|
|
|
ok_ret( 1, ImmUnlockIMC( params.himc[0] ) );
|
|
|
|
tmp_ctx = ImmLockIMC( params.himc[1] );
|
|
ok_eq( params.contexts[1], tmp_ctx, INPUTCONTEXT *, "%p" );
|
|
ret = ImmGetIMCLockCount( params.himc[1] );
|
|
ok( ret >= 2, "got ret %u\n", ret );
|
|
ok_ret( 1, ImmUnlockIMC( params.himc[1] ) );
|
|
|
|
/* ImmSetActiveContext should succeed with cross thread HIMC */
|
|
|
|
SET_ENABLE( WM_IME_SETCONTEXT_DEACTIVATE, TRUE );
|
|
SET_ENABLE( WM_IME_SETCONTEXT_ACTIVATE, TRUE );
|
|
|
|
SET_EXPECT( WM_IME_SETCONTEXT_ACTIVATE );
|
|
ok_ret( 1, ImmSetActiveContext( hwnd, params.himc[0], TRUE ) );
|
|
CHECK_CALLED( WM_IME_SETCONTEXT_ACTIVATE );
|
|
|
|
SET_EXPECT( WM_IME_SETCONTEXT_DEACTIVATE );
|
|
ok_ret( 1, ImmSetActiveContext( hwnd, params.himc[0], FALSE ) );
|
|
CHECK_CALLED( WM_IME_SETCONTEXT_DEACTIVATE );
|
|
|
|
SET_ENABLE( WM_IME_SETCONTEXT_DEACTIVATE, FALSE );
|
|
SET_ENABLE( WM_IME_SETCONTEXT_ACTIVATE, FALSE );
|
|
|
|
/* ImmSetOpenStatus should fail with cross thread HIMC */
|
|
|
|
ok_ret( 1, ImmSetOpenStatus( himc[1], 0xdeadbeef ) );
|
|
ok_ret( (int)0xdeadbeef, ImmGetOpenStatus( himc[1] ) );
|
|
|
|
ok_ret( 0, ImmSetOpenStatus( params.himc[0], TRUE ) );
|
|
ok_ret( 0, ImmSetOpenStatus( params.himc[1], TRUE ) );
|
|
ok_ret( (int)0xdeadbeef, ImmGetOpenStatus( params.himc[0] ) );
|
|
ok_ret( (int)0xfeedcafe, ImmGetOpenStatus( params.himc[1] ) );
|
|
ok_ret( 0, ImmSetOpenStatus( params.himc[0], FALSE ) );
|
|
ok_ret( (int)0xdeadbeef, ImmGetOpenStatus( params.himc[0] ) );
|
|
|
|
/* ImmSetConversionStatus should fail with cross thread HIMC */
|
|
|
|
ok_ret( 1, ImmGetConversionStatus( himc[1], &conversion, &sentence ) );
|
|
ok_ret( 1, ImmSetConversionStatus( himc[1], conversion, sentence ) );
|
|
|
|
ok_ret( 1, ImmGetConversionStatus( params.himc[0], &conversion, &sentence ) );
|
|
ok_ret( 1, ImmGetConversionStatus( params.himc[1], &conversion, &sentence ) );
|
|
ok_ret( 0, ImmSetConversionStatus( params.himc[0], conversion, sentence ) );
|
|
ok_ret( 0, ImmSetConversionStatus( params.himc[1], conversion, sentence ) );
|
|
|
|
/* ImmSetCompositionFont(W|A) should fail with cross thread HIMC */
|
|
|
|
ok_ret( 1, ImmSetCompositionFontA( himc[1], &fontA ) );
|
|
ok_ret( 1, ImmGetCompositionFontA( himc[1], &fontA ) );
|
|
ok_ret( 1, ImmSetCompositionFontW( himc[1], &fontW ) );
|
|
ok_ret( 1, ImmGetCompositionFontW( himc[1], &fontW ) );
|
|
|
|
ok_ret( 0, ImmSetCompositionFontA( params.himc[0], &fontA ) );
|
|
ok_ret( 0, ImmSetCompositionFontA( params.himc[1], &fontA ) );
|
|
ok_ret( 1, ImmGetCompositionFontA( params.himc[0], &fontA ) );
|
|
ok_ret( 1, ImmGetCompositionFontA( params.himc[1], &fontA ) );
|
|
ok_ret( 0, ImmSetCompositionFontW( params.himc[0], &fontW ) );
|
|
ok_ret( 0, ImmSetCompositionFontW( params.himc[1], &fontW ) );
|
|
ok_ret( 1, ImmGetCompositionFontW( params.himc[0], &fontW ) );
|
|
ok_ret( 1, ImmGetCompositionFontW( params.himc[1], &fontW ) );
|
|
|
|
/* ImmRequestMessage(W|A) should fail with cross thread HIMC */
|
|
|
|
ok_ret( 0, ImmRequestMessageW( himc[1], IMR_COMPOSITIONFONT, (LPARAM)&fontW ) );
|
|
ok_ret( 0, ImmRequestMessageA( himc[1], IMR_COMPOSITIONFONT, (LPARAM)&fontA ) );
|
|
|
|
ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_COMPOSITIONFONT, (LPARAM)&fontW ) );
|
|
ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_COMPOSITIONFONT, (LPARAM)&fontA ) );
|
|
ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_COMPOSITIONFONT, (LPARAM)&fontW ) );
|
|
ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_COMPOSITIONFONT, (LPARAM)&fontA ) );
|
|
|
|
ok_seq( empty_sequence );
|
|
|
|
/* ImmSetCompositionString(W|A) should fail with cross thread HIMC */
|
|
|
|
ok_ret( 10, ImmGetCompositionStringA( himc[1], GCS_COMPSTR, buffer, sizeof(buffer) ) );
|
|
ok_ret( 20, ImmGetCompositionStringW( himc[1], GCS_COMPSTR, buffer, sizeof(buffer) ) );
|
|
ok_ret( 1, ImmSetCompositionStringA( himc[1], SCS_SETSTR, "a", 2, NULL, 0 ) );
|
|
ok_ret( 1, ImmSetCompositionStringW( himc[1], SCS_SETSTR, L"a", 4, NULL, 0 ) );
|
|
|
|
ok_ret( 0, ImmSetCompositionStringA( params.himc[0], SCS_SETSTR, "a", 2, NULL, 0 ) );
|
|
ok_ret( 0, ImmSetCompositionStringA( params.himc[1], SCS_SETSTR, "a", 2, NULL, 0 ) );
|
|
ok_ret( 0, ImmSetCompositionStringW( params.himc[0], SCS_SETSTR, L"a", 4, NULL, 0 ) );
|
|
ok_ret( 0, ImmSetCompositionStringW( params.himc[1], SCS_SETSTR, L"a", 4, NULL, 0 ) );
|
|
ok_ret( 10, ImmGetCompositionStringA( params.himc[0], GCS_COMPSTR, buffer, sizeof(buffer) ) );
|
|
ok_ret( 0, ImmGetCompositionStringA( params.himc[1], GCS_COMPSTR, buffer, sizeof(buffer) ) );
|
|
ok_ret( 20, ImmGetCompositionStringW( params.himc[0], GCS_COMPSTR, buffer, sizeof(buffer) ) );
|
|
ok_ret( 0, ImmGetCompositionStringW( params.himc[1], GCS_COMPSTR, buffer, sizeof(buffer) ) );
|
|
|
|
/* ImmRequestMessage(W|A) should fail with cross thread HIMC */
|
|
|
|
ok_ret( 0, ImmRequestMessageW( himc[1], IMR_RECONVERTSTRING, 0 ) );
|
|
ok_ret( 0, ImmRequestMessageW( himc[1], IMR_RECONVERTSTRING, (LPARAM)&reconv ) );
|
|
ok_ret( 0, ImmRequestMessageA( himc[1], IMR_RECONVERTSTRING, 0 ) );
|
|
ok_ret( 0, ImmRequestMessageA( himc[1], IMR_RECONVERTSTRING, (LPARAM)&reconv ) );
|
|
|
|
ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_RECONVERTSTRING, 0 ) );
|
|
ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_RECONVERTSTRING, (LPARAM)&reconv ) );
|
|
ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_RECONVERTSTRING, 0 ) );
|
|
ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_RECONVERTSTRING, (LPARAM)&reconv ) );
|
|
ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_RECONVERTSTRING, 0 ) );
|
|
ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_RECONVERTSTRING, (LPARAM)&reconv ) );
|
|
ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_RECONVERTSTRING, 0 ) );
|
|
ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_RECONVERTSTRING, (LPARAM)&reconv ) );
|
|
|
|
ok_ret( 0, ImmRequestMessageW( himc[1], IMR_DOCUMENTFEED, 0 ) );
|
|
ok_ret( 0, ImmRequestMessageW( himc[1], IMR_DOCUMENTFEED, (LPARAM)&reconv ) );
|
|
ok_ret( 0, ImmRequestMessageA( himc[1], IMR_DOCUMENTFEED, 0 ) );
|
|
ok_ret( 0, ImmRequestMessageA( himc[1], IMR_DOCUMENTFEED, (LPARAM)&reconv ) );
|
|
|
|
ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_DOCUMENTFEED, 0 ) );
|
|
ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_DOCUMENTFEED, (LPARAM)&reconv ) );
|
|
ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_DOCUMENTFEED, 0 ) );
|
|
ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_DOCUMENTFEED, (LPARAM)&reconv ) );
|
|
ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_DOCUMENTFEED, 0 ) );
|
|
ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_DOCUMENTFEED, (LPARAM)&reconv ) );
|
|
ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_DOCUMENTFEED, 0 ) );
|
|
ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_DOCUMENTFEED, (LPARAM)&reconv ) );
|
|
|
|
ok_ret( 0, ImmRequestMessageW( himc[1], IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) );
|
|
ok_ret( 0, ImmRequestMessageA( himc[1], IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) );
|
|
|
|
ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) );
|
|
ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) );
|
|
ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) );
|
|
ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) );
|
|
|
|
ok_seq( empty_sequence );
|
|
|
|
/* ImmSetCompositionWindow should fail with cross thread HIMC */
|
|
|
|
ok_ret( 1, ImmSetCompositionWindow( himc[1], &composition ) );
|
|
ok_ret( 1, ImmGetCompositionWindow( himc[1], &composition ) );
|
|
|
|
ok_ret( 0, ImmSetCompositionWindow( params.himc[0], &composition ) );
|
|
ok_ret( 0, ImmSetCompositionWindow( params.himc[1], &composition ) );
|
|
ok_ret( 1, ImmGetCompositionWindow( params.himc[0], &composition ) );
|
|
ok_ret( 1, ImmGetCompositionWindow( params.himc[1], &composition ) );
|
|
|
|
/* ImmRequestMessage(W|A) should fail with cross thread HIMC */
|
|
|
|
ok_ret( 0, ImmRequestMessageW( himc[1], IMR_COMPOSITIONWINDOW, (LPARAM)&composition ) );
|
|
ok_ret( 0, ImmRequestMessageA( himc[1], IMR_COMPOSITIONWINDOW, (LPARAM)&composition ) );
|
|
|
|
ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_COMPOSITIONWINDOW, (LPARAM)&composition ) );
|
|
ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_COMPOSITIONWINDOW, (LPARAM)&composition ) );
|
|
ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_COMPOSITIONWINDOW, (LPARAM)&composition ) );
|
|
ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_COMPOSITIONWINDOW, (LPARAM)&composition ) );
|
|
|
|
ok_seq( empty_sequence );
|
|
|
|
/* ImmSetCandidateWindow should fail with cross thread HIMC */
|
|
|
|
ok_ret( 1, ImmSetCandidateWindow( himc[1], &candidate ) );
|
|
ok_ret( 1, ImmGetCandidateWindow( himc[1], 0, &candidate ) );
|
|
|
|
ok_ret( 1, ImmGetCandidateWindow( params.himc[0], 1, &candidate ) );
|
|
ok_ret( 1, ImmGetCandidateWindow( params.himc[1], 1, &candidate ) );
|
|
ok_ret( 0, ImmSetCandidateWindow( params.himc[0], &candidate ) );
|
|
ok_ret( 0, ImmSetCandidateWindow( params.himc[1], &candidate ) );
|
|
|
|
/* ImmRequestMessage(W|A) should fail with cross thread HIMC */
|
|
|
|
candidate.dwIndex = -1;
|
|
ok_ret( 0, ImmRequestMessageW( himc[1], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) );
|
|
ok_ret( 0, ImmRequestMessageA( himc[1], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) );
|
|
|
|
candidate.dwIndex = 0;
|
|
ok_ret( 0, ImmRequestMessageW( himc[1], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) );
|
|
ok_ret( 0, ImmRequestMessageA( himc[1], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) );
|
|
|
|
ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) );
|
|
ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) );
|
|
ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) );
|
|
ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) );
|
|
|
|
ok_seq( empty_sequence );
|
|
|
|
/* ImmSetStatusWindowPos should fail with cross thread HIMC */
|
|
|
|
ok_ret( 1, ImmSetStatusWindowPos( himc[1], &pos ) );
|
|
ok_ret( 1, ImmGetStatusWindowPos( himc[1], &pos ) );
|
|
|
|
ok_ret( 0, ImmSetStatusWindowPos( params.himc[0], &pos ) );
|
|
ok_ret( 0, ImmSetStatusWindowPos( params.himc[1], &pos ) );
|
|
ok_ret( 1, ImmGetStatusWindowPos( params.himc[0], &pos ) );
|
|
ok_ret( 1, ImmGetStatusWindowPos( params.himc[1], &pos ) );
|
|
|
|
/* ImmRequestMessage(W|A) should fail with cross thread HIMC */
|
|
|
|
ok_ret( 0, ImmRequestMessageW( himc[1], IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) );
|
|
ok_ret( 0, ImmRequestMessageA( himc[1], IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) );
|
|
|
|
ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) );
|
|
ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) );
|
|
ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) );
|
|
ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) );
|
|
|
|
ok_seq( empty_sequence );
|
|
|
|
/* ImmGenerateMessage should fail with cross thread HIMC */
|
|
|
|
ok_ret( 1, ImmGenerateMessage( himc[1] ) );
|
|
|
|
ok_ret( 0, ImmGenerateMessage( params.himc[0] ) );
|
|
ok_ret( 0, ImmGenerateMessage( params.himc[1] ) );
|
|
|
|
/* ImmAssociateContext should fail with cross thread HWND or HIMC */
|
|
|
|
tmp_himc = ImmAssociateContext( hwnd, params.himc[0] );
|
|
ok_eq( NULL, tmp_himc, HIMC, "%p" );
|
|
tmp_himc = ImmGetContext( hwnd );
|
|
ok_eq( himc[0], tmp_himc, HIMC, "%p" );
|
|
ok_ret( 1, ImmReleaseContext( hwnd, tmp_himc ) );
|
|
|
|
tmp_himc = ImmAssociateContext( hwnd, params.himc[1] );
|
|
ok_eq( NULL, tmp_himc, HIMC, "%p" );
|
|
tmp_himc = ImmGetContext( hwnd );
|
|
ok_eq( himc[0], tmp_himc, HIMC, "%p" );
|
|
ok_ret( 1, ImmReleaseContext( hwnd, tmp_himc ) );
|
|
|
|
tmp_himc = ImmAssociateContext( params.hwnd, params.himc[1] );
|
|
ok_eq( NULL, tmp_himc, HIMC, "%p" );
|
|
tmp_himc = ImmGetContext( params.hwnd );
|
|
ok_eq( params.himc[0], tmp_himc, HIMC, "%p" );
|
|
ok_ret( 1, ImmReleaseContext( params.hwnd, tmp_himc ) );
|
|
|
|
/* ImmAssociateContext should succeed with cross thread HWND and NULL HIMC */
|
|
|
|
tmp_himc = ImmAssociateContext( params.hwnd, NULL );
|
|
ok_eq( params.himc[0], tmp_himc, HIMC, "%p" );
|
|
tmp_himc = ImmGetContext( params.hwnd );
|
|
ok_eq( NULL, tmp_himc, HIMC, "%p" );
|
|
|
|
/* ImmReleaseContext / ImmDestroyContext should fail with cross thread HIMC */
|
|
|
|
ok_ret( 1, ImmReleaseContext( params.hwnd, params.himc[0] ) );
|
|
ok_ret( 0, ImmDestroyContext( params.himc[1] ) );
|
|
|
|
/* ImmGetContext should fail with another process HWND */
|
|
|
|
tmp_himc = ImmGetContext( GetDesktopWindow() );
|
|
ok_eq( NULL, tmp_himc, HIMC, "%p" );
|
|
|
|
ok_ret( 0, SendMessageW( params.hwnd, WM_CLOSE, 0, 0 ) );
|
|
ok_ret( 1, PostThreadMessageW( tid, WM_QUIT, 1, 0 ) );
|
|
ok_ret( 0, WaitForSingleObject( thread, 5000 ) );
|
|
ok_ret( 1, CloseHandle( thread ) );
|
|
ok_ret( 1, CloseHandle( params.event ) );
|
|
|
|
ok_ret( 1, ImmReleaseContext( hwnd, himc[0] ) );
|
|
ok_ret( 1, ImmDestroyContext( himc[1] ) );
|
|
}
|
|
|
|
static void test_ImmIsUIMessage(void)
|
|
{
|
|
struct test
|
|
{
|
|
UINT msg;
|
|
BOOL ret;
|
|
};
|
|
|
|
static const struct test tests[] =
|
|
{
|
|
{ WM_MOUSEMOVE, FALSE },
|
|
{ WM_IME_STARTCOMPOSITION, TRUE },
|
|
{ WM_IME_ENDCOMPOSITION, TRUE },
|
|
{ WM_IME_COMPOSITION, TRUE },
|
|
{ WM_IME_SETCONTEXT, TRUE },
|
|
{ WM_IME_NOTIFY, TRUE },
|
|
{ WM_IME_CONTROL, FALSE },
|
|
{ WM_IME_COMPOSITIONFULL, TRUE },
|
|
{ WM_IME_SELECT, TRUE },
|
|
{ WM_IME_CHAR, FALSE },
|
|
{ 0x287 /* FIXME */, TRUE },
|
|
{ WM_IME_REQUEST, FALSE },
|
|
{ WM_IME_KEYDOWN, FALSE },
|
|
{ WM_IME_KEYUP, FALSE },
|
|
{ 0, FALSE } /* mark the end */
|
|
};
|
|
|
|
UINT WM_MSIME_SERVICE = RegisterWindowMessageA("MSIMEService");
|
|
UINT WM_MSIME_RECONVERTOPTIONS = RegisterWindowMessageA("MSIMEReconvertOptions");
|
|
UINT WM_MSIME_MOUSE = RegisterWindowMessageA("MSIMEMouseOperation");
|
|
UINT WM_MSIME_RECONVERTREQUEST = RegisterWindowMessageA("MSIMEReconvertRequest");
|
|
UINT WM_MSIME_RECONVERT = RegisterWindowMessageA("MSIMEReconvert");
|
|
UINT WM_MSIME_QUERYPOSITION = RegisterWindowMessageA("MSIMEQueryPosition");
|
|
UINT WM_MSIME_DOCUMENTFEED = RegisterWindowMessageA("MSIMEDocumentFeed");
|
|
|
|
const struct test *test;
|
|
BOOL ret;
|
|
|
|
if (!pImmIsUIMessageA) return;
|
|
|
|
for (test = tests; test->msg; test++)
|
|
{
|
|
msg_spy_flush_msgs();
|
|
ret = pImmIsUIMessageA(NULL, test->msg, 0, 0);
|
|
ok(ret == test->ret, "ImmIsUIMessageA returned %x for %x\n", ret, test->msg);
|
|
ok(!msg_spy_find_msg(test->msg), "Windows does not send 0x%x for NULL hwnd\n", test->msg);
|
|
|
|
ret = pImmIsUIMessageA(hwnd, test->msg, 0, 0);
|
|
ok(ret == test->ret, "ImmIsUIMessageA returned %x for %x\n", ret, test->msg);
|
|
if (ret)
|
|
ok(msg_spy_find_msg(test->msg) != NULL, "Windows does send 0x%x\n", test->msg);
|
|
else
|
|
ok(!msg_spy_find_msg(test->msg), "Windows does not send 0x%x\n", test->msg);
|
|
}
|
|
|
|
ret = pImmIsUIMessageA(NULL, WM_MSIME_SERVICE, 0, 0);
|
|
ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_SERVICE\n");
|
|
ret = pImmIsUIMessageA(NULL, WM_MSIME_RECONVERTOPTIONS, 0, 0);
|
|
ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_RECONVERTOPTIONS\n");
|
|
ret = pImmIsUIMessageA(NULL, WM_MSIME_MOUSE, 0, 0);
|
|
ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_MOUSE\n");
|
|
ret = pImmIsUIMessageA(NULL, WM_MSIME_RECONVERTREQUEST, 0, 0);
|
|
ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_RECONVERTREQUEST\n");
|
|
ret = pImmIsUIMessageA(NULL, WM_MSIME_RECONVERT, 0, 0);
|
|
ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_RECONVERT\n");
|
|
ret = pImmIsUIMessageA(NULL, WM_MSIME_QUERYPOSITION, 0, 0);
|
|
ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_QUERYPOSITION\n");
|
|
ret = pImmIsUIMessageA(NULL, WM_MSIME_DOCUMENTFEED, 0, 0);
|
|
ok(!ret, "ImmIsUIMessageA returned TRUE for WM_MSIME_DOCUMENTFEED\n");
|
|
}
|
|
|
|
static void test_ImmGetContext(void)
|
|
{
|
|
HIMC himc;
|
|
DWORD err;
|
|
|
|
SetLastError(0xdeadbeef);
|
|
himc = ImmGetContext((HWND)0xffffffff);
|
|
err = GetLastError();
|
|
ok(himc == NULL, "ImmGetContext succeeded\n");
|
|
ok(err == ERROR_INVALID_WINDOW_HANDLE, "got %lu\n", err);
|
|
|
|
himc = ImmGetContext(hwnd);
|
|
ok(himc != NULL, "ImmGetContext failed\n");
|
|
ok(ImmReleaseContext(hwnd, himc), "ImmReleaseContext failed\n");
|
|
}
|
|
|
|
static LRESULT (WINAPI *old_imm_wnd_proc)(HWND, UINT, WPARAM, LPARAM);
|
|
static LRESULT WINAPI imm_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
|
{
|
|
ok(msg != WM_DESTROY, "got WM_DESTROY message\n");
|
|
return old_imm_wnd_proc(hwnd, msg, wparam, lparam);
|
|
}
|
|
|
|
static HWND thread_ime_wnd;
|
|
static DWORD WINAPI test_ImmGetDefaultIMEWnd_thread(void *arg)
|
|
{
|
|
CreateWindowA("static", "static", WS_POPUP, 0, 0, 1, 1, NULL, NULL, NULL, NULL);
|
|
|
|
thread_ime_wnd = ImmGetDefaultIMEWnd(0);
|
|
ok(thread_ime_wnd != 0, "ImmGetDefaultIMEWnd returned NULL\n");
|
|
old_imm_wnd_proc = (void*)SetWindowLongPtrW(thread_ime_wnd, GWLP_WNDPROC, (LONG_PTR)imm_wnd_proc);
|
|
return 0;
|
|
}
|
|
|
|
static void test_ImmDefaultHwnd(void)
|
|
{
|
|
HIMC imc1, imc2, imc3;
|
|
HWND def1, def3;
|
|
HANDLE thread;
|
|
HWND hwnd;
|
|
char title[16];
|
|
LONG style;
|
|
|
|
hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "Wine imm32.dll test",
|
|
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
|
|
240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
|
|
|
|
ShowWindow(hwnd, SW_SHOWNORMAL);
|
|
|
|
imc1 = ImmGetContext(hwnd);
|
|
if (!imc1)
|
|
{
|
|
win_skip("IME support not implemented\n");
|
|
return;
|
|
}
|
|
|
|
def1 = ImmGetDefaultIMEWnd(hwnd);
|
|
|
|
GetWindowTextA(def1, title, sizeof(title));
|
|
ok(!strcmp(title, "Default IME"), "got %s\n", title);
|
|
style = GetWindowLongA(def1, GWL_STYLE);
|
|
ok(style == (WS_DISABLED | WS_POPUP | WS_CLIPSIBLINGS), "got %08lx\n", style);
|
|
style = GetWindowLongA(def1, GWL_EXSTYLE);
|
|
ok(style == 0, "got %08lx\n", style);
|
|
|
|
imc2 = ImmCreateContext();
|
|
ImmSetOpenStatus(imc2, TRUE);
|
|
|
|
imc3 = ImmGetContext(hwnd);
|
|
def3 = ImmGetDefaultIMEWnd(hwnd);
|
|
|
|
ok(def3 == def1, "Default IME window should not change\n");
|
|
ok(imc1 == imc3, "IME context should not change\n");
|
|
ImmSetOpenStatus(imc2, FALSE);
|
|
|
|
thread = CreateThread(NULL, 0, test_ImmGetDefaultIMEWnd_thread, NULL, 0, NULL);
|
|
WaitForSingleObject(thread, INFINITE);
|
|
ok(thread_ime_wnd != def1, "thread_ime_wnd == def1\n");
|
|
ok(!IsWindow(thread_ime_wnd), "thread_ime_wnd was not destroyed\n");
|
|
CloseHandle(thread);
|
|
|
|
ImmReleaseContext(hwnd, imc1);
|
|
ImmReleaseContext(hwnd, imc3);
|
|
ImmDestroyContext(imc2);
|
|
DestroyWindow(hwnd);
|
|
}
|
|
|
|
static BOOL CALLBACK is_ime_window_proc(HWND hWnd, LPARAM param)
|
|
{
|
|
WCHAR class_nameW[16];
|
|
HWND *ime_window = (HWND *)param;
|
|
if (GetClassNameW(hWnd, class_nameW, ARRAY_SIZE(class_nameW)) && !lstrcmpW(class_nameW, L"IME"))
|
|
{
|
|
*ime_window = hWnd;
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static HWND get_ime_window(void)
|
|
{
|
|
HWND ime_window = NULL;
|
|
EnumThreadWindows(GetCurrentThreadId(), is_ime_window_proc, (LPARAM)&ime_window);
|
|
return ime_window;
|
|
}
|
|
|
|
struct testcase_ime_window {
|
|
BOOL visible;
|
|
BOOL top_level_window;
|
|
};
|
|
|
|
static DWORD WINAPI test_default_ime_window_cb(void *arg)
|
|
{
|
|
struct testcase_ime_window *testcase = (struct testcase_ime_window *)arg;
|
|
DWORD visible = testcase->visible ? WS_VISIBLE : 0;
|
|
HWND hwnd1, hwnd2, default_ime_wnd, ime_wnd;
|
|
|
|
ok(!get_ime_window(), "Expected no IME windows\n");
|
|
if (testcase->top_level_window) {
|
|
test_phase = FIRST_WINDOW;
|
|
hwnd1 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
|
|
WS_OVERLAPPEDWINDOW | visible,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
|
|
}
|
|
else {
|
|
hwnd1 = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "Wine imm32.dll test",
|
|
WS_CHILD | visible,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
240, 24, hwnd, NULL, GetModuleHandleW(NULL), NULL);
|
|
}
|
|
ime_wnd = get_ime_window();
|
|
ok(ime_wnd != NULL, "Expected IME window existence\n");
|
|
default_ime_wnd = ImmGetDefaultIMEWnd(hwnd1);
|
|
ok(ime_wnd == default_ime_wnd, "Expected %p, got %p\n", ime_wnd, default_ime_wnd);
|
|
|
|
test_phase = SECOND_WINDOW;
|
|
hwnd2 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
|
|
WS_OVERLAPPEDWINDOW | visible,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
|
|
DestroyWindow(hwnd2);
|
|
ok(IsWindow(ime_wnd) ||
|
|
broken(!testcase->visible /* Vista */) ||
|
|
broken(!testcase->top_level_window /* Vista */) ,
|
|
"Expected IME window existence\n");
|
|
DestroyWindow(hwnd1);
|
|
flaky
|
|
ok(!IsWindow(ime_wnd), "Expected no IME windows\n");
|
|
return 1;
|
|
}
|
|
|
|
static DWORD WINAPI test_default_ime_window_cancel_cb(void *arg)
|
|
{
|
|
struct testcase_ime_window *testcase = (struct testcase_ime_window *)arg;
|
|
DWORD visible = testcase->visible ? WS_VISIBLE : 0;
|
|
HWND hwnd1, hwnd2, default_ime_wnd, ime_wnd;
|
|
|
|
ok(!get_ime_window(), "Expected no IME windows\n");
|
|
test_phase = NCCREATE_CANCEL;
|
|
hwnd1 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
|
|
WS_OVERLAPPEDWINDOW | visible,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
|
|
ok(hwnd1 == NULL, "creation succeeded, got %p\n", hwnd1);
|
|
ok(!get_ime_window(), "Expected no IME windows\n");
|
|
|
|
test_phase = CREATE_CANCEL;
|
|
hwnd1 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
|
|
WS_OVERLAPPEDWINDOW | visible,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
|
|
ok(hwnd1 == NULL, "creation succeeded, got %p\n", hwnd1);
|
|
ok(!get_ime_window(), "Expected no IME windows\n");
|
|
|
|
test_phase = FIRST_WINDOW;
|
|
hwnd2 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
|
|
WS_OVERLAPPEDWINDOW | visible,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
|
|
ime_wnd = get_ime_window();
|
|
ok(ime_wnd != NULL, "Expected IME window existence\n");
|
|
default_ime_wnd = ImmGetDefaultIMEWnd(hwnd2);
|
|
ok(ime_wnd == default_ime_wnd, "Expected %p, got %p\n", ime_wnd, default_ime_wnd);
|
|
|
|
DestroyWindow(hwnd2);
|
|
ok(!IsWindow(ime_wnd), "Expected no IME windows\n");
|
|
return 1;
|
|
}
|
|
|
|
static DWORD WINAPI test_default_ime_disabled_cb(void *arg)
|
|
{
|
|
HWND hWnd, default_ime_wnd;
|
|
|
|
ok(!get_ime_window(), "Expected no IME windows\n");
|
|
ImmDisableIME(GetCurrentThreadId());
|
|
test_phase = IME_DISABLED;
|
|
hWnd = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
|
|
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
|
|
default_ime_wnd = ImmGetDefaultIMEWnd(hWnd);
|
|
ok(!default_ime_wnd, "Expected no IME windows\n");
|
|
DestroyWindow(hWnd);
|
|
return 1;
|
|
}
|
|
|
|
static DWORD WINAPI test_default_ime_with_message_only_window_cb(void *arg)
|
|
{
|
|
HWND hwnd1, hwnd2, default_ime_wnd;
|
|
|
|
/* Message-only window doesn't create associated IME window. */
|
|
test_phase = PHASE_UNKNOWN;
|
|
hwnd1 = CreateWindowA(wndcls, "Wine imm32.dll test",
|
|
WS_OVERLAPPEDWINDOW,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
240, 120, HWND_MESSAGE, NULL, GetModuleHandleW(NULL), NULL);
|
|
default_ime_wnd = ImmGetDefaultIMEWnd(hwnd1);
|
|
ok(!default_ime_wnd, "Expected no IME windows, got %p\n", default_ime_wnd);
|
|
|
|
/* Setting message-only window as owner at creation,
|
|
doesn't create associated IME window. */
|
|
hwnd2 = CreateWindowA(wndcls, "Wine imm32.dll test",
|
|
WS_OVERLAPPEDWINDOW,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
240, 120, hwnd1, NULL, GetModuleHandleW(NULL), NULL);
|
|
default_ime_wnd = ImmGetDefaultIMEWnd(hwnd2);
|
|
todo_wine ok(!default_ime_wnd || broken(IsWindow(default_ime_wnd)), "Expected no IME windows, got %p\n", default_ime_wnd);
|
|
|
|
DestroyWindow(hwnd2);
|
|
DestroyWindow(hwnd1);
|
|
|
|
/* Making window message-only after creation,
|
|
doesn't disassociate IME window. */
|
|
hwnd1 = CreateWindowA(wndcls, "Wine imm32.dll test",
|
|
WS_OVERLAPPEDWINDOW,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL);
|
|
default_ime_wnd = ImmGetDefaultIMEWnd(hwnd1);
|
|
ok(IsWindow(default_ime_wnd), "Expected IME window existence\n");
|
|
SetParent(hwnd1, HWND_MESSAGE);
|
|
default_ime_wnd = ImmGetDefaultIMEWnd(hwnd1);
|
|
ok(IsWindow(default_ime_wnd), "Expected IME window existence\n");
|
|
DestroyWindow(hwnd1);
|
|
return 1;
|
|
}
|
|
|
|
static void test_default_ime_window_creation(void)
|
|
{
|
|
HANDLE thread;
|
|
size_t i;
|
|
struct testcase_ime_window testcases[] = {
|
|
/* visible, top-level window */
|
|
{ TRUE, TRUE },
|
|
{ FALSE, TRUE },
|
|
{ TRUE, FALSE },
|
|
{ FALSE, FALSE }
|
|
};
|
|
|
|
for (i = 0; i < ARRAY_SIZE(testcases); i++)
|
|
{
|
|
thread = CreateThread(NULL, 0, test_default_ime_window_cb, &testcases[i], 0, NULL);
|
|
ok(thread != NULL, "CreateThread failed with error %lu\n", GetLastError());
|
|
while (MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_ALLINPUT) == WAIT_OBJECT_0 + 1)
|
|
{
|
|
MSG msg;
|
|
while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessageA(&msg);
|
|
}
|
|
}
|
|
CloseHandle(thread);
|
|
|
|
if (testcases[i].top_level_window)
|
|
{
|
|
thread = CreateThread(NULL, 0, test_default_ime_window_cancel_cb, &testcases[i], 0, NULL);
|
|
ok(thread != NULL, "CreateThread failed with error %lu\n", GetLastError());
|
|
WaitForSingleObject(thread, INFINITE);
|
|
CloseHandle(thread);
|
|
}
|
|
}
|
|
|
|
thread = CreateThread(NULL, 0, test_default_ime_disabled_cb, NULL, 0, NULL);
|
|
WaitForSingleObject(thread, INFINITE);
|
|
CloseHandle(thread);
|
|
|
|
thread = CreateThread(NULL, 0, test_default_ime_with_message_only_window_cb, NULL, 0, NULL);
|
|
WaitForSingleObject(thread, INFINITE);
|
|
CloseHandle(thread);
|
|
|
|
test_phase = PHASE_UNKNOWN;
|
|
}
|
|
|
|
static void test_ImmGetIMCLockCount(void)
|
|
{
|
|
HIMC imc;
|
|
DWORD count, ret, i;
|
|
INPUTCONTEXT *ic;
|
|
|
|
imc = ImmCreateContext();
|
|
ImmDestroyContext(imc);
|
|
SetLastError(0xdeadbeef);
|
|
count = ImmGetIMCLockCount((HIMC)0xdeadcafe);
|
|
ok(count == 0, "Invalid IMC should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
count = ImmGetIMCLockCount(0x00000000);
|
|
ok(count == 0, "NULL IMC should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == 0xdeadbeef, "Last Error should remain unchanged: %08lx\n",ret);
|
|
count = ImmGetIMCLockCount(imc);
|
|
ok(count == 0, "Destroyed IMC should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
|
|
imc = ImmCreateContext();
|
|
count = ImmGetIMCLockCount(imc);
|
|
ok(count == 0, "expect 0, returned %ld\n", count);
|
|
ic = ImmLockIMC(imc);
|
|
ok(ic != NULL, "ImmLockIMC failed!\n");
|
|
count = ImmGetIMCLockCount(imc);
|
|
ok(count == 1, "expect 1, returned %ld\n", count);
|
|
ret = ImmUnlockIMC(imc);
|
|
ok(ret == TRUE, "expect TRUE, ret %ld\n", ret);
|
|
count = ImmGetIMCLockCount(imc);
|
|
ok(count == 0, "expect 0, returned %ld\n", count);
|
|
ret = ImmUnlockIMC(imc);
|
|
ok(ret == TRUE, "expect TRUE, ret %ld\n", ret);
|
|
count = ImmGetIMCLockCount(imc);
|
|
ok(count == 0, "expect 0, returned %ld\n", count);
|
|
|
|
for (i = 0; i < GMEM_LOCKCOUNT * 2; i++)
|
|
{
|
|
ic = ImmLockIMC(imc);
|
|
ok(ic != NULL, "ImmLockIMC failed!\n");
|
|
}
|
|
count = ImmGetIMCLockCount(imc);
|
|
todo_wine ok(count == GMEM_LOCKCOUNT, "expect GMEM_LOCKCOUNT, returned %ld\n", count);
|
|
|
|
for (i = 0; i < GMEM_LOCKCOUNT - 1; i++)
|
|
ImmUnlockIMC(imc);
|
|
count = ImmGetIMCLockCount(imc);
|
|
todo_wine ok(count == 1, "expect 1, returned %ld\n", count);
|
|
ImmUnlockIMC(imc);
|
|
count = ImmGetIMCLockCount(imc);
|
|
todo_wine ok(count == 0, "expect 0, returned %ld\n", count);
|
|
|
|
ImmDestroyContext(imc);
|
|
}
|
|
|
|
static void test_ImmGetIMCCLockCount(void)
|
|
{
|
|
HIMCC imcc;
|
|
DWORD count, g_count, i;
|
|
BOOL ret;
|
|
VOID *p;
|
|
|
|
imcc = ImmCreateIMCC(sizeof(CANDIDATEINFO));
|
|
count = ImmGetIMCCLockCount(imcc);
|
|
ok(count == 0, "expect 0, returned %ld\n", count);
|
|
ImmLockIMCC(imcc);
|
|
count = ImmGetIMCCLockCount(imcc);
|
|
ok(count == 1, "expect 1, returned %ld\n", count);
|
|
ret = ImmUnlockIMCC(imcc);
|
|
ok(ret == FALSE, "expect FALSE, ret %d\n", ret);
|
|
count = ImmGetIMCCLockCount(imcc);
|
|
ok(count == 0, "expect 0, returned %ld\n", count);
|
|
ret = ImmUnlockIMCC(imcc);
|
|
ok(ret == FALSE, "expect FALSE, ret %d\n", ret);
|
|
count = ImmGetIMCCLockCount(imcc);
|
|
ok(count == 0, "expect 0, returned %ld\n", count);
|
|
|
|
p = ImmLockIMCC(imcc);
|
|
ok(GlobalHandle(p) == imcc, "expect %p, returned %p\n", imcc, GlobalHandle(p));
|
|
|
|
for (i = 0; i < GMEM_LOCKCOUNT * 2; i++)
|
|
{
|
|
ImmLockIMCC(imcc);
|
|
count = ImmGetIMCCLockCount(imcc);
|
|
g_count = GlobalFlags(imcc) & GMEM_LOCKCOUNT;
|
|
ok(count == g_count, "count %ld, g_count %ld\n", count, g_count);
|
|
}
|
|
count = ImmGetIMCCLockCount(imcc);
|
|
ok(count == GMEM_LOCKCOUNT, "expect GMEM_LOCKCOUNT, returned %ld\n", count);
|
|
|
|
for (i = 0; i < GMEM_LOCKCOUNT - 1; i++)
|
|
GlobalUnlock(imcc);
|
|
count = ImmGetIMCCLockCount(imcc);
|
|
ok(count == 1, "expect 1, returned %ld\n", count);
|
|
GlobalUnlock(imcc);
|
|
count = ImmGetIMCCLockCount(imcc);
|
|
ok(count == 0, "expect 0, returned %ld\n", count);
|
|
|
|
ImmDestroyIMCC(imcc);
|
|
}
|
|
|
|
static void test_ImmDestroyContext(void)
|
|
{
|
|
HIMC imc;
|
|
DWORD ret, count;
|
|
INPUTCONTEXT *ic;
|
|
|
|
imc = ImmCreateContext();
|
|
count = ImmGetIMCLockCount(imc);
|
|
ok(count == 0, "expect 0, returned %ld\n", count);
|
|
ic = ImmLockIMC(imc);
|
|
ok(ic != NULL, "ImmLockIMC failed!\n");
|
|
count = ImmGetIMCLockCount(imc);
|
|
ok(count == 1, "expect 1, returned %ld\n", count);
|
|
ret = ImmDestroyContext(imc);
|
|
ok(ret == TRUE, "Destroy a locked IMC should success!\n");
|
|
ic = ImmLockIMC(imc);
|
|
ok(ic == NULL, "Lock a destroyed IMC should fail!\n");
|
|
ret = ImmUnlockIMC(imc);
|
|
ok(ret == FALSE, "Unlock a destroyed IMC should fail!\n");
|
|
count = ImmGetIMCLockCount(imc);
|
|
ok(count == 0, "Get lock count of a destroyed IMC should return 0!\n");
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmDestroyContext(imc);
|
|
ok(ret == FALSE, "Destroy a destroyed IMC should fail!\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
}
|
|
|
|
static void test_ImmDestroyIMCC(void)
|
|
{
|
|
HIMCC imcc;
|
|
DWORD ret, count, size;
|
|
VOID *p;
|
|
|
|
imcc = ImmCreateIMCC(sizeof(CANDIDATEINFO));
|
|
count = ImmGetIMCCLockCount(imcc);
|
|
ok(count == 0, "expect 0, returned %ld\n", count);
|
|
p = ImmLockIMCC(imcc);
|
|
ok(p != NULL, "ImmLockIMCC failed!\n");
|
|
count = ImmGetIMCCLockCount(imcc);
|
|
ok(count == 1, "expect 1, returned %ld\n", count);
|
|
size = ImmGetIMCCSize(imcc);
|
|
ok(size == sizeof(CANDIDATEINFO), "returned %ld\n", size);
|
|
p = ImmDestroyIMCC(imcc);
|
|
ok(p == NULL, "Destroy a locked IMCC should success!\n");
|
|
p = ImmLockIMCC(imcc);
|
|
ok(p == NULL, "Lock a destroyed IMCC should fail!\n");
|
|
ret = ImmUnlockIMCC(imcc);
|
|
ok(ret == FALSE, "Unlock a destroyed IMCC should return FALSE!\n");
|
|
count = ImmGetIMCCLockCount(imcc);
|
|
ok(count == 0, "Get lock count of a destroyed IMCC should return 0!\n");
|
|
size = ImmGetIMCCSize(imcc);
|
|
ok(size == 0, "Get size of a destroyed IMCC should return 0!\n");
|
|
SetLastError(0xdeadbeef);
|
|
p = ImmDestroyIMCC(imcc);
|
|
ok(p != NULL, "returned NULL\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
}
|
|
|
|
static void test_ImmMessages(void)
|
|
{
|
|
CANDIDATEFORM cf;
|
|
imm_msgs *msg;
|
|
HWND defwnd;
|
|
HIMC imc;
|
|
UINT idx = 0;
|
|
|
|
LPINPUTCONTEXT lpIMC;
|
|
LPTRANSMSG lpTransMsg;
|
|
|
|
HWND hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "Wine imm32.dll test",
|
|
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
|
|
240, 120, NULL, NULL, GetModuleHandleA(NULL), NULL);
|
|
|
|
ShowWindow(hwnd, SW_SHOWNORMAL);
|
|
defwnd = ImmGetDefaultIMEWnd(hwnd);
|
|
imc = ImmGetContext(hwnd);
|
|
|
|
ImmSetOpenStatus(imc, TRUE);
|
|
msg_spy_flush_msgs();
|
|
SendMessageA(defwnd, WM_IME_CONTROL, IMC_GETCANDIDATEPOS, (LPARAM)&cf );
|
|
do
|
|
{
|
|
msg = msg_spy_find_next_msg(WM_IME_CONTROL,&idx);
|
|
if (msg) ok(!msg->post, "Message should not be posted\n");
|
|
} while (msg);
|
|
msg_spy_flush_msgs();
|
|
|
|
lpIMC = ImmLockIMC(imc);
|
|
lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + 1) * sizeof(TRANSMSG));
|
|
lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf);
|
|
lpTransMsg += lpIMC->dwNumMsgBuf;
|
|
lpTransMsg->message = WM_IME_STARTCOMPOSITION;
|
|
lpTransMsg->wParam = 0;
|
|
lpTransMsg->lParam = 0;
|
|
ImmUnlockIMCC(lpIMC->hMsgBuf);
|
|
lpIMC->dwNumMsgBuf++;
|
|
ImmUnlockIMC(imc);
|
|
ImmGenerateMessage(imc);
|
|
idx = 0;
|
|
do
|
|
{
|
|
msg = msg_spy_find_next_msg(WM_IME_STARTCOMPOSITION, &idx);
|
|
if (msg) ok(!msg->post, "Message should not be posted\n");
|
|
} while (msg);
|
|
msg_spy_flush_msgs();
|
|
|
|
lpIMC = ImmLockIMC(imc);
|
|
lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + 1) * sizeof(TRANSMSG));
|
|
lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf);
|
|
lpTransMsg += lpIMC->dwNumMsgBuf;
|
|
lpTransMsg->message = WM_IME_COMPOSITION;
|
|
lpTransMsg->wParam = 0;
|
|
lpTransMsg->lParam = 0;
|
|
ImmUnlockIMCC(lpIMC->hMsgBuf);
|
|
lpIMC->dwNumMsgBuf++;
|
|
ImmUnlockIMC(imc);
|
|
ImmGenerateMessage(imc);
|
|
idx = 0;
|
|
do
|
|
{
|
|
msg = msg_spy_find_next_msg(WM_IME_COMPOSITION, &idx);
|
|
if (msg) ok(!msg->post, "Message should not be posted\n");
|
|
} while (msg);
|
|
msg_spy_flush_msgs();
|
|
|
|
lpIMC = ImmLockIMC(imc);
|
|
lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + 1) * sizeof(TRANSMSG));
|
|
lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf);
|
|
lpTransMsg += lpIMC->dwNumMsgBuf;
|
|
lpTransMsg->message = WM_IME_ENDCOMPOSITION;
|
|
lpTransMsg->wParam = 0;
|
|
lpTransMsg->lParam = 0;
|
|
ImmUnlockIMCC(lpIMC->hMsgBuf);
|
|
lpIMC->dwNumMsgBuf++;
|
|
ImmUnlockIMC(imc);
|
|
ImmGenerateMessage(imc);
|
|
idx = 0;
|
|
do
|
|
{
|
|
msg = msg_spy_find_next_msg(WM_IME_ENDCOMPOSITION, &idx);
|
|
if (msg) ok(!msg->post, "Message should not be posted\n");
|
|
} while (msg);
|
|
msg_spy_flush_msgs();
|
|
|
|
ImmSetOpenStatus(imc, FALSE);
|
|
ImmReleaseContext(hwnd, imc);
|
|
DestroyWindow(hwnd);
|
|
}
|
|
|
|
static LRESULT CALLBACK processkey_wnd_proc( HWND hWnd, UINT msg, WPARAM wParam,
|
|
LPARAM lParam )
|
|
{
|
|
return DefWindowProcW(hWnd, msg, wParam, lParam);
|
|
}
|
|
|
|
static void test_ime_processkey(void)
|
|
{
|
|
MSG msg;
|
|
WNDCLASSW wclass;
|
|
HANDLE hInstance = GetModuleHandleW(NULL);
|
|
TEST_INPUT inputs[2];
|
|
HIMC imc;
|
|
INT rc;
|
|
HWND hWndTest;
|
|
|
|
wclass.lpszClassName = L"ProcessKeyTestClass";
|
|
wclass.style = CS_HREDRAW | CS_VREDRAW;
|
|
wclass.lpfnWndProc = processkey_wnd_proc;
|
|
wclass.hInstance = hInstance;
|
|
wclass.hIcon = LoadIconW(0, (LPCWSTR)IDI_APPLICATION);
|
|
wclass.hCursor = LoadCursorW( NULL, (LPCWSTR)IDC_ARROW);
|
|
wclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
|
wclass.lpszMenuName = 0;
|
|
wclass.cbClsExtra = 0;
|
|
wclass.cbWndExtra = 0;
|
|
if(!RegisterClassW(&wclass)){
|
|
win_skip("Failed to register window.\n");
|
|
return;
|
|
}
|
|
|
|
/* create the test window that will receive the keystrokes */
|
|
hWndTest = CreateWindowW(wclass.lpszClassName, L"ProcessKey",
|
|
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 100, 100,
|
|
NULL, NULL, hInstance, NULL);
|
|
|
|
ShowWindow(hWndTest, SW_SHOW);
|
|
SetWindowPos(hWndTest, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
|
|
SetForegroundWindow(hWndTest);
|
|
UpdateWindow(hWndTest);
|
|
|
|
imc = ImmGetContext(hWndTest);
|
|
if (!imc)
|
|
{
|
|
win_skip("IME not supported\n");
|
|
DestroyWindow(hWndTest);
|
|
return;
|
|
}
|
|
|
|
rc = ImmSetOpenStatus(imc, TRUE);
|
|
if (rc != TRUE)
|
|
{
|
|
win_skip("Unable to open IME\n");
|
|
ImmReleaseContext(hWndTest, imc);
|
|
DestroyWindow(hWndTest);
|
|
return;
|
|
}
|
|
|
|
/* flush pending messages */
|
|
while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageW(&msg);
|
|
|
|
SetFocus(hWndTest);
|
|
|
|
/* init input data that never changes */
|
|
inputs[1].type = inputs[0].type = INPUT_KEYBOARD;
|
|
inputs[1].u.ki.dwExtraInfo = inputs[0].u.ki.dwExtraInfo = 0;
|
|
inputs[1].u.ki.time = inputs[0].u.ki.time = 0;
|
|
|
|
/* Pressing a key */
|
|
inputs[0].u.ki.wVk = 0x41;
|
|
inputs[0].u.ki.wScan = 0x1e;
|
|
inputs[0].u.ki.dwFlags = 0x0;
|
|
|
|
pSendInput(1, (INPUT*)inputs, sizeof(INPUT));
|
|
|
|
while(PeekMessageW(&msg, hWndTest, 0, 0, PM_NOREMOVE)) {
|
|
if(msg.message != WM_KEYDOWN)
|
|
PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
|
|
else
|
|
{
|
|
ok(msg.wParam != VK_PROCESSKEY,"Incorrect ProcessKey Found\n");
|
|
PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
|
|
if(msg.wParam == VK_PROCESSKEY)
|
|
trace("ProcessKey was correctly found\n");
|
|
}
|
|
TranslateMessage(&msg);
|
|
/* test calling TranslateMessage multiple times */
|
|
TranslateMessage(&msg);
|
|
DispatchMessageW(&msg);
|
|
}
|
|
|
|
inputs[0].u.ki.wVk = 0x41;
|
|
inputs[0].u.ki.wScan = 0x1e;
|
|
inputs[0].u.ki.dwFlags = KEYEVENTF_KEYUP;
|
|
|
|
pSendInput(1, (INPUT*)inputs, sizeof(INPUT));
|
|
|
|
while(PeekMessageW(&msg, hWndTest, 0, 0, PM_NOREMOVE)) {
|
|
if(msg.message != WM_KEYUP)
|
|
PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
|
|
else
|
|
{
|
|
ok(msg.wParam != VK_PROCESSKEY,"Incorrect ProcessKey Found\n");
|
|
PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
|
|
ok(msg.wParam != VK_PROCESSKEY,"ProcessKey should still not be Found\n");
|
|
}
|
|
TranslateMessage(&msg);
|
|
DispatchMessageW(&msg);
|
|
}
|
|
|
|
ImmReleaseContext(hWndTest, imc);
|
|
ImmSetOpenStatus(imc, FALSE);
|
|
DestroyWindow(hWndTest);
|
|
}
|
|
|
|
static void test_InvalidIMC(void)
|
|
{
|
|
HIMC imc_destroy;
|
|
HIMC imc_null = 0x00000000;
|
|
HIMC imc_bad = (HIMC)0xdeadcafe;
|
|
|
|
HIMC imc1, imc2, oldimc;
|
|
DWORD ret;
|
|
DWORD count;
|
|
CHAR buffer[1000];
|
|
INPUTCONTEXT *ic;
|
|
LOGFONTA lf;
|
|
BOOL r;
|
|
|
|
memset(&lf, 0, sizeof(lf));
|
|
|
|
imc_destroy = ImmCreateContext();
|
|
ret = ImmDestroyContext(imc_destroy);
|
|
ok(ret == TRUE, "Destroy an IMC should success!\n");
|
|
|
|
/* Test associating destroyed imc */
|
|
imc1 = ImmGetContext(hwnd);
|
|
SetLastError(0xdeadbeef);
|
|
oldimc = ImmAssociateContext(hwnd, imc_destroy);
|
|
ok(!oldimc, "Associating to a destroyed imc should fail!\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
imc2 = ImmGetContext(hwnd);
|
|
ok(imc1 == imc2, "imc should not changed! imc1 %p, imc2 %p\n", imc1, imc2);
|
|
|
|
SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, TRUE);
|
|
SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, TRUE);
|
|
r = ImmSetActiveContext(hwnd, imc_destroy, TRUE);
|
|
ok(!r, "ImmSetActiveContext succeeded\n");
|
|
SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE);
|
|
r = ImmSetActiveContext(hwnd, imc_destroy, FALSE);
|
|
ok(r, "ImmSetActiveContext failed\n");
|
|
CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE);
|
|
SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, FALSE);
|
|
SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, FALSE);
|
|
|
|
/* Test associating NULL imc, which is different from an invalid imc */
|
|
oldimc = ImmAssociateContext(hwnd, imc_null);
|
|
ok(oldimc != NULL, "Associating to NULL imc should success!\n");
|
|
imc2 = ImmGetContext(hwnd);
|
|
ok(!imc2, "expect NULL, returned %p\n", imc2);
|
|
oldimc = ImmAssociateContext(hwnd, imc1);
|
|
ok(!oldimc, "expect NULL, returned %p\n", oldimc);
|
|
imc2 = ImmGetContext(hwnd);
|
|
ok(imc2 == imc1, "imc should not changed! imc2 %p, imc1 %p\n", imc2, imc1);
|
|
|
|
/* Test associating invalid imc */
|
|
imc1 = ImmGetContext(hwnd);
|
|
SetLastError(0xdeadbeef);
|
|
oldimc = ImmAssociateContext(hwnd, imc_bad);
|
|
ok(!oldimc, "Associating to a destroyed imc should fail!\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
imc2 = ImmGetContext(hwnd);
|
|
ok(imc1 == imc2, "imc should not changed! imc1 %p, imc2 %p\n", imc1, imc2);
|
|
|
|
|
|
/* Test ImmGetCandidateListA */
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetCandidateListA(imc_bad, 0, NULL, 0);
|
|
ok(ret == 0, "Bad IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetCandidateListA(imc_null, 0, NULL, 0);
|
|
ok(ret == 0, "NULL IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetCandidateListA(imc_destroy, 0, NULL, 0);
|
|
ok(ret == 0, "Destroyed IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
|
|
/* Test ImmGetCandidateListCountA*/
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetCandidateListCountA(imc_bad,&count);
|
|
ok(ret == 0, "Bad IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetCandidateListCountA(imc_null,&count);
|
|
ok(ret == 0, "NULL IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetCandidateListCountA(imc_destroy,&count);
|
|
ok(ret == 0, "Destroyed IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
|
|
/* Test ImmGetCandidateWindow */
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetCandidateWindow(imc_bad, 0, (LPCANDIDATEFORM)buffer);
|
|
ok(ret == 0, "Bad IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetCandidateWindow(imc_null, 0, (LPCANDIDATEFORM)buffer);
|
|
ok(ret == 0, "NULL IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetCandidateWindow(imc_destroy, 0, (LPCANDIDATEFORM)buffer);
|
|
ok(ret == 0, "Destroyed IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
|
|
/* Test ImmGetCompositionFontA */
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetCompositionFontA(imc_bad, (LPLOGFONTA)buffer);
|
|
ok(ret == 0, "Bad IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetCompositionFontA(imc_null, (LPLOGFONTA)buffer);
|
|
ok(ret == 0, "NULL IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetCompositionFontA(imc_destroy, (LPLOGFONTA)buffer);
|
|
ok(ret == 0, "Destroyed IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
|
|
/* Test ImmGetCompositionWindow */
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetCompositionWindow(imc_bad, (LPCOMPOSITIONFORM)buffer);
|
|
ok(ret == 0, "Bad IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetCompositionWindow(imc_null, (LPCOMPOSITIONFORM)buffer);
|
|
ok(ret == 0, "NULL IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetCompositionWindow(imc_destroy, (LPCOMPOSITIONFORM)buffer);
|
|
ok(ret == 0, "Destroyed IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
|
|
/* Test ImmGetCompositionStringA */
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetCompositionStringA(imc_bad, GCS_COMPSTR, NULL, 0);
|
|
ok(ret == 0, "Bad IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetCompositionStringA(imc_null, GCS_COMPSTR, NULL, 0);
|
|
ok(ret == 0, "NULL IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetCompositionStringA(imc_destroy, GCS_COMPSTR, NULL, 0);
|
|
ok(ret == 0, "Destroyed IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
|
|
/* Test ImmSetOpenStatus */
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmSetOpenStatus(imc_bad, 1);
|
|
ok(ret == 0, "Bad IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmSetOpenStatus(imc_null, 1);
|
|
ok(ret == 0, "NULL IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmSetOpenStatus(imc_destroy, 1);
|
|
ok(ret == 0, "Destroyed IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
|
|
/* Test ImmGetOpenStatus */
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetOpenStatus(imc_bad);
|
|
ok(ret == 0, "Bad IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetOpenStatus(imc_null);
|
|
ok(ret == 0, "NULL IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetOpenStatus(imc_destroy);
|
|
ok(ret == 0, "Destroyed IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
|
|
/* Test ImmGetStatusWindowPos */
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetStatusWindowPos(imc_bad, NULL);
|
|
ok(ret == 0, "Bad IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetStatusWindowPos(imc_null, NULL);
|
|
ok(ret == 0, "NULL IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetStatusWindowPos(imc_destroy, NULL);
|
|
ok(ret == 0, "Destroyed IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
|
|
/* Test ImmRequestMessageA */
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmRequestMessageA(imc_bad, WM_CHAR, 0);
|
|
ok(ret == 0, "Bad IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmRequestMessageA(imc_null, WM_CHAR, 0);
|
|
ok(ret == 0, "NULL IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmRequestMessageA(imc_destroy, WM_CHAR, 0);
|
|
ok(ret == 0, "Destroyed IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
|
|
/* Test ImmSetCompositionFontA */
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmSetCompositionFontA(imc_bad, &lf);
|
|
ok(ret == 0, "Bad IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmSetCompositionFontA(imc_null, &lf);
|
|
ok(ret == 0, "NULL IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmSetCompositionFontA(imc_destroy, &lf);
|
|
ok(ret == 0, "Destroyed IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
|
|
/* Test ImmSetCompositionWindow */
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmSetCompositionWindow(imc_bad, NULL);
|
|
ok(ret == 0, "Bad IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmSetCompositionWindow(imc_null, NULL);
|
|
ok(ret == 0, "NULL IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmSetCompositionWindow(imc_destroy, NULL);
|
|
ok(ret == 0, "Destroyed IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
|
|
/* Test ImmSetConversionStatus */
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmSetConversionStatus(imc_bad, 0, 0);
|
|
ok(ret == 0, "Bad IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmSetConversionStatus(imc_null, 0, 0);
|
|
ok(ret == 0, "NULL IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmSetConversionStatus(imc_destroy, 0, 0);
|
|
ok(ret == 0, "Destroyed IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
|
|
/* Test ImmSetStatusWindowPos */
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmSetStatusWindowPos(imc_bad, 0);
|
|
ok(ret == 0, "Bad IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmSetStatusWindowPos(imc_null, 0);
|
|
ok(ret == 0, "NULL IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmSetStatusWindowPos(imc_destroy, 0);
|
|
ok(ret == 0, "Destroyed IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
|
|
/* Test ImmGetImeMenuItemsA */
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetImeMenuItemsA(imc_bad, 0, 0, NULL, NULL, 0);
|
|
ok(ret == 0, "Bad IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetImeMenuItemsA(imc_null, 0, 0, NULL, NULL, 0);
|
|
ok(ret == 0, "NULL IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGetImeMenuItemsA(imc_destroy, 0, 0, NULL, NULL, 0);
|
|
ok(ret == 0, "Destroyed IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
|
|
/* Test ImmLockIMC */
|
|
SetLastError(0xdeadbeef);
|
|
ic = ImmLockIMC(imc_bad);
|
|
ok(ic == 0, "Bad IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ic = ImmLockIMC(imc_null);
|
|
ok(ic == 0, "NULL IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ic = ImmLockIMC(imc_destroy);
|
|
ok(ic == 0, "Destroyed IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
|
|
/* Test ImmUnlockIMC */
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmUnlockIMC(imc_bad);
|
|
ok(ret == 0, "Bad IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmUnlockIMC(imc_null);
|
|
ok(ret == 0, "NULL IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == 0xdeadbeef, "last error should remain unchanged %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmUnlockIMC(imc_destroy);
|
|
ok(ret == 0, "Destroyed IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
|
|
/* Test ImmGenerateMessage */
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGenerateMessage(imc_bad);
|
|
ok(ret == 0, "Bad IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGenerateMessage(imc_null);
|
|
ok(ret == 0, "NULL IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = ImmGenerateMessage(imc_destroy);
|
|
ok(ret == 0, "Destroyed IME should return 0\n");
|
|
ret = GetLastError();
|
|
ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08lx!\n", ret);
|
|
}
|
|
|
|
#define test_apttype(apttype) _test_apttype(apttype, __LINE__)
|
|
static void _test_apttype(APTTYPE apttype, unsigned int line)
|
|
{
|
|
APTTYPEQUALIFIER qualifier;
|
|
HRESULT hr, hr_expected;
|
|
APTTYPE type;
|
|
|
|
hr = CoGetApartmentType(&type, &qualifier);
|
|
hr_expected = (apttype == -1 ? CO_E_NOTINITIALIZED : S_OK);
|
|
ok_(__FILE__, line)(hr == hr_expected, "CoGetApartmentType returned %lx\n", hr);
|
|
if (FAILED(hr))
|
|
return;
|
|
|
|
ok_(__FILE__, line)(type == apttype, "type %x\n", type);
|
|
ok_(__FILE__, line)(!qualifier, "qualifier %x\n", qualifier);
|
|
}
|
|
|
|
static DWORD WINAPI com_initialization_thread(void *arg)
|
|
{
|
|
HRESULT hr;
|
|
BOOL r;
|
|
|
|
test_apttype(-1);
|
|
ImmDisableIME(GetCurrentThreadId());
|
|
r = ImmSetActiveContext(NULL, NULL, TRUE);
|
|
ok(r, "ImmSetActiveContext failed\n");
|
|
test_apttype(APTTYPE_MAINSTA);
|
|
hr = CoInitialize(NULL);
|
|
ok(hr == S_OK, "CoInitialize returned %lx\n", hr);
|
|
CoUninitialize();
|
|
test_apttype(-1);
|
|
|
|
/* Changes IMM behavior so it no longer initialized COM */
|
|
r = ImmSetActiveContext(NULL, NULL, TRUE);
|
|
ok(r, "ImmSetActiveContext failed\n");
|
|
test_apttype(APTTYPE_MAINSTA);
|
|
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
ok(hr == S_OK, "CoInitialize returned %lx\n", hr);
|
|
test_apttype(APTTYPE_MTA);
|
|
CoUninitialize();
|
|
test_apttype(-1);
|
|
r = ImmSetActiveContext(NULL, NULL, TRUE);
|
|
ok(r, "ImmSetActiveContext failed\n");
|
|
test_apttype(-1);
|
|
return 0;
|
|
}
|
|
|
|
static void test_com_initialization(void)
|
|
{
|
|
APTTYPEQUALIFIER qualifier;
|
|
HANDLE thread;
|
|
APTTYPE type;
|
|
HRESULT hr;
|
|
HWND wnd;
|
|
BOOL r;
|
|
|
|
thread = CreateThread(NULL, 0, com_initialization_thread, NULL, 0, NULL);
|
|
ok(thread != NULL, "CreateThread failed\n");
|
|
WaitForSingleObject(thread, INFINITE);
|
|
CloseHandle(thread);
|
|
|
|
test_apttype(-1);
|
|
r = ImmSetActiveContext(NULL, (HIMC)0xdeadbeef, TRUE);
|
|
ok(!r, "ImmSetActiveContext succeeded\n");
|
|
test_apttype(-1);
|
|
|
|
r = ImmSetActiveContext(NULL, NULL, TRUE);
|
|
ok(r, "ImmSetActiveContext failed\n");
|
|
test_apttype(APTTYPE_MAINSTA);
|
|
|
|
/* Force default IME window destruction */
|
|
wnd = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
ok(wnd != NULL, "CreateWindow failed\n");
|
|
DestroyWindow(wnd);
|
|
test_apttype(-1);
|
|
|
|
r = ImmSetActiveContext(NULL, NULL, TRUE);
|
|
ok(r, "ImmSetActiveContext failed\n");
|
|
test_apttype(APTTYPE_MAINSTA);
|
|
hr = CoInitialize(NULL);
|
|
ok(hr == S_OK, "CoInitialize returned %lx\n", hr);
|
|
CoUninitialize();
|
|
test_apttype(-1);
|
|
|
|
/* Test with default IME window created */
|
|
wnd = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
ok(wnd != NULL, "CreateWindow failed\n");
|
|
test_apttype(-1);
|
|
r = ImmSetActiveContext(NULL, NULL, TRUE);
|
|
ok(r, "ImmSetActiveContext failed\n");
|
|
test_apttype(APTTYPE_MAINSTA);
|
|
hr = CoInitialize(NULL);
|
|
ok(hr == S_OK, "CoInitialize returned %lx\n", hr);
|
|
CoUninitialize();
|
|
test_apttype(APTTYPE_MAINSTA);
|
|
DestroyWindow(wnd);
|
|
test_apttype(-1);
|
|
|
|
wnd = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
ok(wnd != NULL, "CreateWindow failed\n");
|
|
r = ImmSetActiveContext(NULL, NULL, TRUE);
|
|
CoUninitialize();
|
|
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
ok(hr == S_OK, "CoInitialize returned %lx\n", hr);
|
|
test_apttype(APTTYPE_MTA);
|
|
DestroyWindow(wnd);
|
|
|
|
hr = CoGetApartmentType(&type, &qualifier);
|
|
ok(hr == CO_E_NOTINITIALIZED || broken(hr == S_OK) /* w10v22H2 */,
|
|
"CoGetApartmentType returned %#lx\n", hr);
|
|
test_apttype(hr == S_OK ? APTTYPE_MTA : -1);
|
|
|
|
wnd = CreateWindowA("static", "static", WS_POPUP, 0, 0, 100, 100, 0, 0, 0, 0);
|
|
ok(wnd != NULL, "CreateWindow failed\n");
|
|
test_apttype(hr == S_OK ? APTTYPE_MTA : -1);
|
|
ShowWindow(wnd, SW_SHOW);
|
|
test_apttype(hr == S_OK ? APTTYPE_MTA : APTTYPE_MAINSTA);
|
|
DestroyWindow(wnd);
|
|
test_apttype(-1);
|
|
}
|
|
|
|
static DWORD WINAPI disable_ime_thread(void *arg)
|
|
{
|
|
HWND h, def;
|
|
MSG msg;
|
|
BOOL r;
|
|
|
|
h = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
ok(h != NULL, "CreateWindow failed\n");
|
|
def = ImmGetDefaultIMEWnd(h);
|
|
ok(def != NULL, "ImmGetDefaultIMEWnd returned NULL\n");
|
|
|
|
r = ImmDisableIME(arg ? GetCurrentThreadId() : 0);
|
|
ok(r, "ImmDisableIME failed\n");
|
|
|
|
if (arg)
|
|
{
|
|
def = ImmGetDefaultIMEWnd(h);
|
|
todo_wine ok(def != NULL, "ImmGetDefaultIMEWnd returned NULL\n");
|
|
while(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
|
|
DispatchMessageA(&msg);
|
|
}
|
|
def = ImmGetDefaultIMEWnd(h);
|
|
ok(!def, "ImmGetDefaultIMEWnd returned %p\n", def);
|
|
return 0;
|
|
}
|
|
|
|
static DWORD WINAPI check_not_disabled_ime_thread(void *arg)
|
|
{
|
|
HWND def, hwnd;
|
|
|
|
WaitForSingleObject(arg, INFINITE);
|
|
hwnd = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
ok(hwnd != NULL, "CreateWindow failed\n");
|
|
def = ImmGetDefaultIMEWnd(hwnd);
|
|
ok(def != NULL, "ImmGetDefaultIMEWnd returned %p\n", def);
|
|
return 0;
|
|
}
|
|
|
|
static DWORD WINAPI disable_ime_process(void *arg)
|
|
{
|
|
BOOL r = ImmDisableIME(-1);
|
|
ok(r, "ImmDisableIME failed\n");
|
|
return 0;
|
|
}
|
|
|
|
static void test_ImmDisableIME(void)
|
|
{
|
|
HANDLE thread, event;
|
|
DWORD tid;
|
|
HWND def, def2;
|
|
BOOL r;
|
|
|
|
def = ImmGetDefaultIMEWnd(hwnd);
|
|
ok(def != NULL, "ImmGetDefaultIMEWnd(hwnd) returned NULL\n");
|
|
|
|
event = CreateEventW(NULL, TRUE, FALSE, FALSE);
|
|
thread = CreateThread(NULL, 0, check_not_disabled_ime_thread, event, 0, &tid);
|
|
ok(thread != NULL, "CreateThread failed\n");
|
|
r = ImmDisableIME(tid);
|
|
ok(!r, "ImmDisableIME(tid) succeeded\n");
|
|
SetEvent(event);
|
|
WaitForSingleObject(thread, INFINITE);
|
|
CloseHandle(thread);
|
|
CloseHandle(event);
|
|
|
|
thread = CreateThread(NULL, 0, disable_ime_thread, 0, 0, NULL);
|
|
ok(thread != NULL, "CreateThread failed\n");
|
|
WaitForSingleObject(thread, INFINITE);
|
|
CloseHandle(thread);
|
|
|
|
thread = CreateThread(NULL, 0, disable_ime_thread, (void*)1, 0, NULL);
|
|
ok(thread != NULL, "CreateThread failed\n");
|
|
WaitForSingleObject(thread, INFINITE);
|
|
CloseHandle(thread);
|
|
|
|
msg_spy_pump_msg_queue();
|
|
thread = CreateThread(NULL, 0, disable_ime_process, 0, 0, NULL);
|
|
ok(thread != NULL, "CreateThread failed\n");
|
|
WaitForSingleObject(thread, INFINITE);
|
|
CloseHandle(thread);
|
|
|
|
ok(IsWindow(def), "not a window\n");
|
|
def2 = ImmGetDefaultIMEWnd(hwnd);
|
|
ok(def2 == def, "ImmGetDefaultIMEWnd(hwnd) returned %p\n", def2);
|
|
ok(IsWindow(def), "not a window\n");
|
|
msg_spy_pump_msg_queue();
|
|
ok(!IsWindow(def), "window is still valid\n");
|
|
def = ImmGetDefaultIMEWnd(hwnd);
|
|
ok(!def, "ImmGetDefaultIMEWnd(hwnd) returned %p\n", def);
|
|
|
|
r = ImmDisableIME(-1);
|
|
ok(r, "ImmDisableIME(-1) failed\n");
|
|
def = ImmGetDefaultIMEWnd(hwnd);
|
|
ok(!def, "ImmGetDefaultIMEWnd(hwnd) returned %p\n", def);
|
|
}
|
|
|
|
static BOOL WINAPI ime_ImeConfigure( HKL hkl, HWND hwnd, DWORD mode, void *data )
|
|
{
|
|
ime_trace( "hkl %p, hwnd %p, mode %lu, data %p\n", hkl, hwnd, mode, data );
|
|
ok( 0, "unexpected call\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
static DWORD WINAPI ime_ImeConversionList( HIMC himc, const WCHAR *source, CANDIDATELIST *dest,
|
|
DWORD dest_len, UINT flag )
|
|
{
|
|
ime_trace( "himc %p, source %s, dest %p, dest_len %lu, flag %#x\n",
|
|
himc, debugstr_w(source), dest, dest_len, flag );
|
|
ok( 0, "unexpected call\n" );
|
|
return 0;
|
|
}
|
|
|
|
static BOOL WINAPI ime_ImeDestroy( UINT force )
|
|
{
|
|
ime_trace( "force %u\n", force );
|
|
|
|
todo_wine_if( todo_ImeDestroy )
|
|
CHECK_EXPECT( ImeDestroy );
|
|
|
|
ok( !force, "got force %u\n", force );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static UINT WINAPI ime_ImeEnumRegisterWord( REGISTERWORDENUMPROCW proc, const WCHAR *reading, DWORD style,
|
|
const WCHAR *string, void *data )
|
|
{
|
|
ime_trace( "proc %p, reading %s, style %lu, string %s, data %p\n",
|
|
proc, debugstr_w(reading), style, debugstr_w(string), data );
|
|
|
|
ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
|
|
CHECK_EXPECT( ImeEnumRegisterWord );
|
|
|
|
if (!style)
|
|
{
|
|
ok_eq( 0, reading, const void *, "%p" );
|
|
ok_eq( 0, string, const void *, "%p" );
|
|
}
|
|
else if (ime_info.fdwProperty & IME_PROP_UNICODE)
|
|
{
|
|
ok_eq( 0xdeadbeef, style, UINT, "%#x" );
|
|
ok_wcs( L"Reading", reading );
|
|
ok_wcs( L"String", string );
|
|
}
|
|
else
|
|
{
|
|
ok_eq( 0xdeadbeef, style, UINT, "%#x" );
|
|
ok_str( "Reading", (char *)reading );
|
|
ok_str( "String", (char *)string );
|
|
}
|
|
|
|
if (style) return proc( reading, style, string, data );
|
|
return 0;
|
|
}
|
|
|
|
static LRESULT WINAPI ime_ImeEscape( HIMC himc, UINT escape, void *data )
|
|
{
|
|
ime_trace( "himc %p, escape %#x, data %p\n", himc, escape, data );
|
|
|
|
ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
|
|
CHECK_EXPECT( ImeEscape );
|
|
|
|
switch (escape)
|
|
{
|
|
case IME_ESC_SET_EUDC_DICTIONARY:
|
|
if (!data) return 4;
|
|
if (ime_info.fdwProperty & IME_PROP_UNICODE)
|
|
ok_wcs( L"EscapeIme", data );
|
|
else
|
|
ok_str( "EscapeIme", data );
|
|
/* fallthrough */
|
|
case IME_ESC_QUERY_SUPPORT:
|
|
case IME_ESC_SEQUENCE_TO_INTERNAL:
|
|
case IME_ESC_GET_EUDC_DICTIONARY:
|
|
case IME_ESC_MAX_KEY:
|
|
case IME_ESC_IME_NAME:
|
|
case IME_ESC_HANJA_MODE:
|
|
case IME_ESC_GETHELPFILENAME:
|
|
if (!data) return 4;
|
|
if (ime_info.fdwProperty & IME_PROP_UNICODE) wcscpy( data, L"ImeEscape" );
|
|
else strcpy( data, "ImeEscape" );
|
|
return 4;
|
|
}
|
|
|
|
ok_eq( 0xdeadbeef, escape, UINT, "%#x" );
|
|
ok_eq( NULL, data, void *, "%p" );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static DWORD WINAPI ime_ImeGetImeMenuItems( HIMC himc, DWORD flags, DWORD type, IMEMENUITEMINFOW *parent,
|
|
IMEMENUITEMINFOW *menu, DWORD size )
|
|
{
|
|
ime_trace( "himc %p, flags %#lx, type %lu, parent %p, menu %p, size %#lx\n",
|
|
himc, flags, type, parent, menu, size );
|
|
ok( 0, "unexpected call\n" );
|
|
return 0;
|
|
}
|
|
|
|
static UINT WINAPI ime_ImeGetRegisterWordStyle( UINT item, STYLEBUFW *style )
|
|
{
|
|
ime_trace( "item %u, style %p\n", item, style );
|
|
|
|
ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
|
|
CHECK_EXPECT( ImeGetRegisterWordStyle );
|
|
|
|
if (!style)
|
|
ok_eq( 16, item, UINT, "%u" );
|
|
else if (ime_info.fdwProperty & IME_PROP_UNICODE)
|
|
{
|
|
STYLEBUFW *styleW = style;
|
|
styleW->dwStyle = 0xdeadbeef;
|
|
wcscpy( styleW->szDescription, L"StyleDescription" );
|
|
}
|
|
else
|
|
{
|
|
STYLEBUFA *styleA = (STYLEBUFA *)style;
|
|
styleA->dwStyle = 0xdeadbeef;
|
|
strcpy( styleA->szDescription, "StyleDescription" );
|
|
}
|
|
|
|
return 0xdeadbeef;
|
|
}
|
|
|
|
static BOOL WINAPI ime_ImeInquire( IMEINFO *info, WCHAR *ui_class, DWORD flags )
|
|
{
|
|
ime_trace( "info %p, ui_class %p, flags %#lx\n", info, ui_class, flags );
|
|
|
|
todo_wine_if( todo_ImeInquire )
|
|
CHECK_EXPECT( ImeInquire );
|
|
|
|
ok( !!info, "got info %p\n", info );
|
|
ok( !!ui_class, "got ui_class %p\n", ui_class );
|
|
ok( !flags, "got flags %#lx\n", flags );
|
|
|
|
*info = ime_info;
|
|
|
|
if (ime_info.fdwProperty & IME_PROP_UNICODE)
|
|
wcscpy( ui_class, ime_ui_class.lpszClassName );
|
|
else
|
|
WideCharToMultiByte( CP_ACP, 0, ime_ui_class.lpszClassName, -1,
|
|
(char *)ui_class, 17, NULL, NULL );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL WINAPI ime_ImeProcessKey( HIMC himc, UINT vkey, LPARAM lparam, BYTE *state )
|
|
{
|
|
struct ime_call call =
|
|
{
|
|
.hkl = GetKeyboardLayout( 0 ), .himc = himc,
|
|
.func = IME_PROCESS_KEY, .process_key = {.vkey = vkey, .lparam = lparam}
|
|
};
|
|
ime_trace( "himc %p, vkey %u, lparam %#Ix, state %p\n",
|
|
himc, vkey, lparam, state );
|
|
ime_calls[ime_call_count++] = call;
|
|
return LOWORD(lparam);
|
|
}
|
|
|
|
static BOOL WINAPI ime_ImeRegisterWord( const WCHAR *reading, DWORD style, const WCHAR *string )
|
|
{
|
|
ime_trace( "reading %s, style %lu, string %s\n", debugstr_w(reading), style, debugstr_w(string) );
|
|
|
|
ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
|
|
CHECK_EXPECT( ImeRegisterWord );
|
|
|
|
if (style) ok_eq( 0xdeadbeef, style, UINT, "%#x" );
|
|
if (ime_info.fdwProperty & IME_PROP_UNICODE)
|
|
{
|
|
if (reading) ok_wcs( L"Reading", reading );
|
|
if (string) ok_wcs( L"String", string );
|
|
}
|
|
else
|
|
{
|
|
if (reading) ok_str( "Reading", (char *)reading );
|
|
if (string) ok_str( "String", (char *)string );
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL WINAPI ime_ImeSelect( HIMC himc, BOOL select )
|
|
{
|
|
struct ime_call call =
|
|
{
|
|
.hkl = GetKeyboardLayout( 0 ), .himc = himc,
|
|
.func = IME_SELECT, .select = select
|
|
};
|
|
INPUTCONTEXT *ctx;
|
|
|
|
ime_trace( "himc %p, select %d\n", himc, select );
|
|
ime_calls[ime_call_count++] = call;
|
|
|
|
if (ImeSelect_init_status && select)
|
|
{
|
|
ctx = ImmLockIMC( himc );
|
|
ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
|
|
ctx->fOpen = ~0;
|
|
ctx->fdwConversion = ~0;
|
|
ctx->fdwSentence = ~0;
|
|
ImmUnlockIMC( himc );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL WINAPI ime_ImeSetActiveContext( HIMC himc, BOOL flag )
|
|
{
|
|
struct ime_call call =
|
|
{
|
|
.hkl = GetKeyboardLayout( 0 ), .himc = himc,
|
|
.func = IME_SET_ACTIVE_CONTEXT, .set_active_context = {.flag = flag}
|
|
};
|
|
ime_trace( "himc %p, flag %#x\n", himc, flag );
|
|
ime_calls[ime_call_count++] = call;
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL WINAPI ime_ImeSetCompositionString( HIMC himc, DWORD index, const void *comp, DWORD comp_len,
|
|
const void *read, DWORD read_len )
|
|
{
|
|
ime_trace( "himc %p, index %lu, comp %p, comp_len %lu, read %p, read_len %lu\n",
|
|
himc, index, comp, comp_len, read, read_len );
|
|
CHECK_EXPECT( ImeSetCompositionString );
|
|
|
|
ok_eq( expect_ime, GetKeyboardLayout( 0 ), HKL, "%p" );
|
|
ok_ne( default_himc, himc, HIMC, "%p" );
|
|
|
|
if (ime_info.fdwProperty & IME_PROP_UNICODE)
|
|
{
|
|
switch (index)
|
|
{
|
|
case SCS_SETSTR:
|
|
todo_wine_if( todo_ImeSetCompositionString )
|
|
ok_eq( 22, comp_len, UINT, "%#x" );
|
|
ok_wcs( L"CompString", comp );
|
|
break;
|
|
case SCS_CHANGECLAUSE:
|
|
{
|
|
const UINT *clause = comp;
|
|
ok_eq( 8, comp_len, UINT, "%#x" );
|
|
ok_eq( 0, clause[0], UINT, "%#x" );
|
|
todo_wine_if( todo_ImeSetCompositionString )
|
|
ok_eq( 1, clause[1], UINT, "%#x");
|
|
break;
|
|
}
|
|
case SCS_CHANGEATTR:
|
|
{
|
|
const BYTE *attr = comp;
|
|
todo_wine_if( todo_ImeSetCompositionString && comp_len != 4 )
|
|
ok_eq( 4, comp_len, UINT, "%#x" );
|
|
todo_wine_if( todo_ImeSetCompositionString && attr[0] != 0xcd )
|
|
ok_eq( 0xcd, attr[0], UINT, "%#x" );
|
|
todo_wine_if( todo_ImeSetCompositionString )
|
|
ok_eq( 0xcd, attr[1], UINT, "%#x" );
|
|
break;
|
|
}
|
|
default:
|
|
ok( 0, "unexpected index %#lx\n", index );
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (index)
|
|
{
|
|
case SCS_SETSTR:
|
|
todo_wine_if( todo_ImeSetCompositionString )
|
|
ok_eq( 11, comp_len, UINT, "%#x" );
|
|
ok_str( "CompString", comp );
|
|
break;
|
|
case SCS_CHANGECLAUSE:
|
|
{
|
|
const UINT *clause = comp;
|
|
ok_eq( 8, comp_len, UINT, "%#x" );
|
|
todo_wine_if( todo_ImeSetCompositionString )
|
|
ok_eq( 0, clause[0], UINT, "%#x" );
|
|
todo_wine_if( todo_ImeSetCompositionString )
|
|
ok_eq( 1, clause[1], UINT, "%#x");
|
|
break;
|
|
}
|
|
case SCS_CHANGEATTR:
|
|
{
|
|
const BYTE *attr = comp;
|
|
todo_wine_if( todo_ImeSetCompositionString && comp_len != 4 )
|
|
ok_eq( 4, comp_len, UINT, "%#x" );
|
|
todo_wine_if( todo_ImeSetCompositionString )
|
|
ok_eq( 0xcd, attr[0], UINT, "%#x" );
|
|
todo_wine_if( todo_ImeSetCompositionString )
|
|
ok_eq( 0xcd, attr[1], UINT, "%#x" );
|
|
break;
|
|
}
|
|
default:
|
|
ok( 0, "unexpected index %#lx\n", index );
|
|
break;
|
|
}
|
|
}
|
|
|
|
ok_eq( NULL, read, const void *, "%p" );
|
|
ok_eq( 0, read_len, UINT, "%#x" );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static UINT WINAPI ime_ImeToAsciiEx( UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, UINT flags, HIMC himc )
|
|
{
|
|
struct ime_call call =
|
|
{
|
|
.hkl = GetKeyboardLayout( 0 ), .himc = himc,
|
|
.func = IME_TO_ASCII_EX, .to_ascii_ex = {.vkey = vkey, .vsc = vsc, .flags = flags}
|
|
};
|
|
INPUTCONTEXT *ctx;
|
|
UINT count = 0;
|
|
|
|
ime_trace( "vkey %#x, vsc %#x, state %p, msgs %p, flags %#x, himc %p\n",
|
|
vkey, vsc, state, msgs, flags, himc );
|
|
ime_calls[ime_call_count++] = call;
|
|
|
|
ok_ne( NULL, msgs, TRANSMSGLIST *, "%p" );
|
|
todo_wine ok_eq( 256, msgs->uMsgCount, UINT, "%u" );
|
|
|
|
ctx = ImmLockIMC( himc );
|
|
ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( ctx->hWnd ) );
|
|
|
|
if (vsc & 0x200)
|
|
{
|
|
msgs->TransMsg[0].message = WM_IME_STARTCOMPOSITION;
|
|
msgs->TransMsg[0].wParam = 1;
|
|
msgs->TransMsg[0].lParam = 0;
|
|
count++;
|
|
msgs->TransMsg[1].message = WM_IME_ENDCOMPOSITION;
|
|
msgs->TransMsg[1].wParam = 1;
|
|
msgs->TransMsg[1].lParam = 0;
|
|
count++;
|
|
}
|
|
|
|
if (vsc & 0x400)
|
|
{
|
|
TRANSMSG *msgs;
|
|
|
|
ctx = ImmLockIMC( himc );
|
|
ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
|
|
|
|
ok_ne( NULL, ctx->hMsgBuf, HIMCC, "%p" );
|
|
ok_eq( 0, ctx->dwNumMsgBuf, UINT, "%u" );
|
|
|
|
ctx->hMsgBuf = ImmReSizeIMCC( ctx->hMsgBuf, 64 * sizeof(*msgs) );
|
|
ok_ne( NULL, ctx->hMsgBuf, HIMCC, "%p" );
|
|
|
|
msgs = ImmLockIMCC( ctx->hMsgBuf );
|
|
ok_ne( NULL, msgs, TRANSMSG *, "%p" );
|
|
|
|
msgs[ctx->dwNumMsgBuf].message = WM_IME_STARTCOMPOSITION;
|
|
msgs[ctx->dwNumMsgBuf].wParam = 2;
|
|
msgs[ctx->dwNumMsgBuf].lParam = 0;
|
|
ctx->dwNumMsgBuf++;
|
|
msgs[ctx->dwNumMsgBuf].message = WM_IME_ENDCOMPOSITION;
|
|
msgs[ctx->dwNumMsgBuf].wParam = 2;
|
|
msgs[ctx->dwNumMsgBuf].lParam = 0;
|
|
ctx->dwNumMsgBuf++;
|
|
|
|
ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) );
|
|
}
|
|
|
|
ok_ret( 1, ImmUnlockIMC( himc ) );
|
|
|
|
if (vsc & 0x800) count = ~0;
|
|
return count;
|
|
}
|
|
|
|
static BOOL WINAPI ime_ImeUnregisterWord( const WCHAR *reading, DWORD style, const WCHAR *string )
|
|
{
|
|
ime_trace( "reading %s, style %lu, string %s\n", debugstr_w(reading), style, debugstr_w(string) );
|
|
|
|
ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
|
|
CHECK_EXPECT( ImeUnregisterWord );
|
|
|
|
if (style) ok_eq( 0xdeadbeef, style, UINT, "%#x" );
|
|
if (ime_info.fdwProperty & IME_PROP_UNICODE)
|
|
{
|
|
if (reading) ok_wcs( L"Reading", reading );
|
|
if (string) ok_wcs( L"String", string );
|
|
}
|
|
else
|
|
{
|
|
if (reading) ok_str( "Reading", (char *)reading );
|
|
if (string) ok_str( "String", (char *)string );
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL WINAPI ime_NotifyIME( HIMC himc, DWORD action, DWORD index, DWORD value )
|
|
{
|
|
struct ime_call call =
|
|
{
|
|
.hkl = GetKeyboardLayout( 0 ), .himc = himc,
|
|
.func = IME_NOTIFY, .notify = {.action = action, .index = index, .value = value}
|
|
};
|
|
ime_trace( "himc %p, action %#lx, index %lu, value %lu\n", himc, action, index, value );
|
|
if (!ignore_IME_NOTIFY) ime_calls[ime_call_count++] = call;
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL WINAPI ime_DllMain( HINSTANCE instance, DWORD reason, LPVOID reserved )
|
|
{
|
|
ime_trace( "instance %p, reason %lu, reserved %p.\n", instance, reason, reserved );
|
|
|
|
switch (reason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
DisableThreadLibraryCalls( instance );
|
|
ime_ui_class.hInstance = instance;
|
|
RegisterClassExW( &ime_ui_class );
|
|
todo_wine_if(todo_IME_DLL_PROCESS_ATTACH)
|
|
CHECK_EXPECT( IME_DLL_PROCESS_ATTACH );
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
UnregisterClassW( ime_ui_class.lpszClassName, instance );
|
|
todo_wine_if(todo_IME_DLL_PROCESS_DETACH)
|
|
CHECK_EXPECT( IME_DLL_PROCESS_DETACH );
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static struct ime_functions ime_functions =
|
|
{
|
|
ime_ImeConfigure,
|
|
ime_ImeConversionList,
|
|
ime_ImeDestroy,
|
|
ime_ImeEnumRegisterWord,
|
|
ime_ImeEscape,
|
|
ime_ImeGetImeMenuItems,
|
|
ime_ImeGetRegisterWordStyle,
|
|
ime_ImeInquire,
|
|
ime_ImeProcessKey,
|
|
ime_ImeRegisterWord,
|
|
ime_ImeSelect,
|
|
ime_ImeSetActiveContext,
|
|
ime_ImeSetCompositionString,
|
|
ime_ImeToAsciiEx,
|
|
ime_ImeUnregisterWord,
|
|
ime_NotifyIME,
|
|
ime_DllMain,
|
|
};
|
|
|
|
static HKL ime_install(void)
|
|
{
|
|
WCHAR buffer[MAX_PATH];
|
|
HMODULE module;
|
|
DWORD len, ret;
|
|
HKEY hkey;
|
|
HKL hkl;
|
|
|
|
/* IME module gets cached and won't reload from disk as soon as a window has
|
|
* loaded it. To workaround the issue we load the module first as a DLL,
|
|
* set its function pointers from the test, and later when the cached IME
|
|
* gets loaded, read the function pointers from the separately loaded DLL.
|
|
*/
|
|
|
|
load_resource( L"ime_wrapper.dll", buffer );
|
|
|
|
SetLastError( 0xdeadbeef );
|
|
ret = MoveFileW( buffer, L"c:\\windows\\system32\\winetest_ime.dll" );
|
|
if (!ret)
|
|
{
|
|
ok( GetLastError() == ERROR_ACCESS_DENIED, "got error %lu\n", GetLastError() );
|
|
win_skip( "Failed to copy DLL to system directory\n" );
|
|
return 0;
|
|
}
|
|
|
|
module = LoadLibraryW( L"c:\\windows\\system32\\winetest_ime.dll" );
|
|
ok( !!module, "LoadLibraryW failed, error %lu\n", GetLastError() );
|
|
*(struct ime_functions *)GetProcAddress( module, "ime_functions" ) = ime_functions;
|
|
|
|
/* install the actual IME module, it will lookup the functions from the DLL */
|
|
load_resource( L"ime_wrapper.dll", buffer );
|
|
|
|
SetLastError( 0xdeadbeef );
|
|
swprintf( ime_path, ARRAY_SIZE(ime_path), L"c:\\windows\\system32\\wine%04x.ime", ime_count++ );
|
|
ret = MoveFileW( buffer, ime_path );
|
|
todo_wine_if( GetLastError() == ERROR_ALREADY_EXISTS )
|
|
ok( ret || broken( !ret ) /* sometimes still in use */,
|
|
"MoveFileW failed, error %lu\n", GetLastError() );
|
|
|
|
hkl = ImmInstallIMEW( ime_path, L"WineTest IME" );
|
|
ok( hkl == expect_ime, "ImmInstallIMEW returned %p, error %lu\n", hkl, GetLastError() );
|
|
|
|
swprintf( buffer, ARRAY_SIZE(buffer), L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\%08x", hkl );
|
|
ret = RegOpenKeyW( HKEY_LOCAL_MACHINE, buffer, &hkey );
|
|
ok( !ret, "RegOpenKeyW returned %#lx, error %lu\n", ret, GetLastError() );
|
|
|
|
len = sizeof(buffer);
|
|
memset( buffer, 0xcd, sizeof(buffer) );
|
|
ret = RegQueryValueExW( hkey, L"Ime File", NULL, NULL, (BYTE *)buffer, &len );
|
|
ok( !ret, "RegQueryValueExW returned %#lx, error %lu\n", ret, GetLastError() );
|
|
ok( !wcsicmp( buffer, wcsrchr( ime_path, '\\' ) + 1 ), "got Ime File %s\n", debugstr_w(buffer) );
|
|
|
|
len = sizeof(buffer);
|
|
memset( buffer, 0xcd, sizeof(buffer) );
|
|
ret = RegQueryValueExW( hkey, L"Layout Text", NULL, NULL, (BYTE *)buffer, &len );
|
|
ok( !ret, "RegQueryValueExW returned %#lx, error %lu\n", ret, GetLastError() );
|
|
ok( !wcscmp( buffer, L"WineTest IME" ), "got Layout Text %s\n", debugstr_w(buffer) );
|
|
|
|
len = sizeof(buffer);
|
|
memset( buffer, 0, sizeof(buffer) );
|
|
ret = RegQueryValueExW( hkey, L"Layout File", NULL, NULL, (BYTE *)buffer, &len );
|
|
todo_wine
|
|
ok( !ret, "RegQueryValueExW returned %#lx, error %lu\n", ret, GetLastError() );
|
|
todo_wine
|
|
ok( !wcscmp( buffer, L"kbdus.dll" ), "got Layout File %s\n", debugstr_w(buffer) );
|
|
|
|
ret = RegCloseKey( hkey );
|
|
ok( !ret, "RegCloseKey returned %#lx, error %lu\n", ret, GetLastError() );
|
|
|
|
return hkl;
|
|
}
|
|
|
|
static void ime_cleanup( HKL hkl, BOOL free )
|
|
{
|
|
HMODULE module = GetModuleHandleW( L"winetest_ime.dll" );
|
|
WCHAR buffer[MAX_PATH], value[MAX_PATH];
|
|
DWORD i, buffer_len, value_len, ret;
|
|
HKEY hkey;
|
|
|
|
ret = UnloadKeyboardLayout( hkl );
|
|
todo_wine
|
|
ok( ret, "UnloadKeyboardLayout failed, error %lu\n", GetLastError() );
|
|
|
|
if (free) ok_ret( 1, ImmFreeLayout( hkl ) );
|
|
|
|
swprintf( buffer, ARRAY_SIZE(buffer), L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\%08x", hkl );
|
|
ret = RegDeleteKeyW( HKEY_LOCAL_MACHINE, buffer );
|
|
ok( !ret, "RegDeleteKeyW returned %#lx, error %lu\n", ret, GetLastError() );
|
|
|
|
ret = RegOpenKeyW( HKEY_CURRENT_USER, L"Keyboard Layout\\Preload", &hkey );
|
|
ok( !ret, "RegOpenKeyW returned %#lx, error %lu\n", ret, GetLastError() );
|
|
|
|
value_len = ARRAY_SIZE(value);
|
|
buffer_len = sizeof(buffer);
|
|
for (i = 0; !RegEnumValueW( hkey, i, value, &value_len, NULL, NULL, (void *)buffer, &buffer_len ); i++)
|
|
{
|
|
value_len = ARRAY_SIZE(value);
|
|
buffer_len = sizeof(buffer);
|
|
if (HandleToUlong( hkl ) != wcstoul( buffer, NULL, 16 )) continue;
|
|
ret = RegDeleteValueW( hkey, value );
|
|
ok( !ret, "RegDeleteValueW returned %#lx, error %lu\n", ret, GetLastError() );
|
|
}
|
|
|
|
ret = RegCloseKey( hkey );
|
|
ok( !ret, "RegCloseKey returned %#lx, error %lu\n", ret, GetLastError() );
|
|
|
|
ret = DeleteFileW( ime_path );
|
|
todo_wine_if( GetLastError() == ERROR_ACCESS_DENIED )
|
|
ok( ret || broken( !ret ) /* sometimes still in use */,
|
|
"DeleteFileW failed, error %lu\n", GetLastError() );
|
|
|
|
ret = FreeLibrary( module );
|
|
ok( ret, "FreeLibrary failed, error %lu\n", GetLastError() );
|
|
|
|
ret = DeleteFileW( L"c:\\windows\\system32\\winetest_ime.dll" );
|
|
ok( ret, "DeleteFileW failed, error %lu\n", GetLastError() );
|
|
}
|
|
|
|
static BOOL CALLBACK enum_get_context( HIMC himc, LPARAM lparam )
|
|
{
|
|
ime_trace( "himc %p\n", himc );
|
|
*(HIMC *)lparam = himc;
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL CALLBACK enum_find_context( HIMC himc, LPARAM lparam )
|
|
{
|
|
ime_trace( "himc %p\n", himc );
|
|
if (lparam && lparam == (LPARAM)himc) return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
static void test_ImmEnumInputContext(void)
|
|
{
|
|
HIMC himc;
|
|
|
|
ok_ret( 1, ImmEnumInputContext( 0, enum_get_context, (LPARAM)&default_himc ) );
|
|
ok_ret( 1, ImmEnumInputContext( -1, enum_find_context, 0 ) );
|
|
ok_ret( 1, ImmEnumInputContext( GetCurrentThreadId(), enum_find_context, 0 ) );
|
|
|
|
todo_wine
|
|
ok_ret( 0, ImmEnumInputContext( 1, enum_find_context, 0 ) );
|
|
todo_wine
|
|
ok_ret( 0, ImmEnumInputContext( GetCurrentProcessId(), enum_find_context, 0 ) );
|
|
|
|
himc = ImmCreateContext();
|
|
ok_ne( NULL, himc, HIMC, "%p" );
|
|
ok_ret( 0, ImmEnumInputContext( GetCurrentThreadId(), enum_find_context, (LPARAM)himc ) );
|
|
ok_ret( 1, ImmDestroyContext( himc ) );
|
|
ok_ret( 1, ImmEnumInputContext( GetCurrentThreadId(), enum_find_context, (LPARAM)himc ) );
|
|
}
|
|
|
|
static void test_ImmInstallIME(void)
|
|
{
|
|
UINT ret;
|
|
HKL hkl;
|
|
|
|
SET_ENABLE( IME_DLL_PROCESS_ATTACH, TRUE );
|
|
SET_ENABLE( ImeInquire, TRUE );
|
|
SET_ENABLE( ImeDestroy, TRUE );
|
|
SET_ENABLE( IME_DLL_PROCESS_DETACH, TRUE );
|
|
|
|
/* IME_PROP_END_UNLOAD for the IME to unload / reload. */
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD;
|
|
|
|
if (!(hkl = ime_install())) goto cleanup;
|
|
|
|
SET_EXPECT( IME_DLL_PROCESS_ATTACH );
|
|
SET_EXPECT( ImeInquire );
|
|
ret = ImmLoadIME( hkl );
|
|
ok( ret, "ImmLoadIME returned %#x\n", ret );
|
|
CHECK_CALLED( IME_DLL_PROCESS_ATTACH );
|
|
CHECK_CALLED( ImeInquire );
|
|
|
|
ret = ImmLoadIME( hkl );
|
|
ok( ret, "ImmLoadIME returned %#x\n", ret );
|
|
|
|
SET_EXPECT( ImeDestroy );
|
|
SET_EXPECT( IME_DLL_PROCESS_DETACH );
|
|
ret = ImmFreeLayout( hkl );
|
|
ok( ret, "ImmFreeLayout returned %#x\n", ret );
|
|
CHECK_CALLED( ImeDestroy );
|
|
CHECK_CALLED( IME_DLL_PROCESS_DETACH );
|
|
|
|
ret = ImmFreeLayout( hkl );
|
|
ok( ret, "ImmFreeLayout returned %#x\n", ret );
|
|
|
|
ime_info.fdwProperty = 0;
|
|
|
|
SET_EXPECT( IME_DLL_PROCESS_ATTACH );
|
|
SET_EXPECT( ImeInquire );
|
|
ret = ImmLoadIME( hkl );
|
|
ok( ret, "ImmLoadIME returned %#x\n", ret );
|
|
CHECK_CALLED( IME_DLL_PROCESS_ATTACH );
|
|
CHECK_CALLED( ImeInquire );
|
|
|
|
ret = ImmLoadIME( hkl );
|
|
ok( ret, "ImmLoadIME returned %#x\n", ret );
|
|
|
|
SET_EXPECT( ImeDestroy );
|
|
SET_EXPECT( IME_DLL_PROCESS_DETACH );
|
|
ret = ImmFreeLayout( hkl );
|
|
ok( ret, "ImmFreeLayout returned %#x\n", ret );
|
|
CHECK_CALLED( ImeDestroy );
|
|
CHECK_CALLED( IME_DLL_PROCESS_DETACH );
|
|
|
|
ret = ImmFreeLayout( hkl );
|
|
ok( ret, "ImmFreeLayout returned %#x\n", ret );
|
|
|
|
ime_cleanup( hkl, FALSE );
|
|
|
|
cleanup:
|
|
SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE );
|
|
SET_ENABLE( ImeInquire, FALSE );
|
|
SET_ENABLE( ImeDestroy, FALSE );
|
|
SET_ENABLE( IME_DLL_PROCESS_DETACH, FALSE );
|
|
}
|
|
|
|
static void test_ImmIsIME(void)
|
|
{
|
|
HKL hkl = GetKeyboardLayout( 0 );
|
|
|
|
SET_ENABLE( IME_DLL_PROCESS_ATTACH, TRUE );
|
|
SET_ENABLE( ImeInquire, TRUE );
|
|
SET_ENABLE( ImeDestroy, TRUE );
|
|
SET_ENABLE( IME_DLL_PROCESS_DETACH, TRUE );
|
|
|
|
SetLastError( 0xdeadbeef );
|
|
ok_ret( 0, ImmIsIME( 0 ) );
|
|
ok_ret( 0xdeadbeef, GetLastError() );
|
|
ok_ret( 1, ImmIsIME( hkl ) );
|
|
|
|
/* IME_PROP_END_UNLOAD for the IME to unload / reload. */
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD;
|
|
|
|
if (!(hkl = wineime_hkl)) goto cleanup;
|
|
|
|
todo_ImeInquire = TRUE;
|
|
todo_ImeDestroy = TRUE;
|
|
todo_IME_DLL_PROCESS_ATTACH = TRUE;
|
|
todo_IME_DLL_PROCESS_DETACH = TRUE;
|
|
ok_ret( 1, ImmIsIME( hkl ) );
|
|
todo_IME_DLL_PROCESS_ATTACH = FALSE;
|
|
todo_IME_DLL_PROCESS_DETACH = FALSE;
|
|
todo_ImeInquire = FALSE;
|
|
todo_ImeDestroy = FALSE;
|
|
|
|
cleanup:
|
|
SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE );
|
|
SET_ENABLE( ImeInquire, FALSE );
|
|
SET_ENABLE( ImeDestroy, FALSE );
|
|
SET_ENABLE( IME_DLL_PROCESS_DETACH, FALSE );
|
|
}
|
|
|
|
static void test_ImmGetProperty(void)
|
|
{
|
|
static const IMEINFO expect_ime_info =
|
|
{
|
|
.fdwProperty = IME_PROP_UNICODE | IME_PROP_AT_CARET,
|
|
};
|
|
static const IMEINFO expect_ime_info_ja_JP = /* MS Japanese IME */
|
|
{
|
|
.fdwProperty = IME_PROP_CANDLIST_START_FROM_1 | IME_PROP_UNICODE | IME_PROP_AT_CARET | 0xa,
|
|
.fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE | IME_CMODE_KATAKANA,
|
|
.fdwSentenceCaps = IME_SMODE_PLAURALCLAUSE | IME_SMODE_CONVERSATION,
|
|
.fdwSCSCaps = SCS_CAP_COMPSTR | SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD,
|
|
.fdwSelectCaps = SELECT_CAP_CONVERSION | SELECT_CAP_SENTENCE,
|
|
.fdwUICaps = UI_CAP_ROT90,
|
|
};
|
|
static const IMEINFO expect_ime_info_ko_KR = /* MS Korean IME */
|
|
{
|
|
.fdwProperty = IME_PROP_CANDLIST_START_FROM_1 | IME_PROP_UNICODE | IME_PROP_AT_CARET | 0xa,
|
|
.fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE,
|
|
.fdwSentenceCaps = IME_SMODE_NONE,
|
|
.fdwSCSCaps = SCS_CAP_COMPSTR | SCS_CAP_SETRECONVERTSTRING,
|
|
.fdwSelectCaps = SELECT_CAP_CONVERSION,
|
|
.fdwUICaps = UI_CAP_ROT90,
|
|
};
|
|
static const IMEINFO expect_ime_info_zh_CN = /* MS Chinese IME */
|
|
{
|
|
.fdwProperty = IME_PROP_CANDLIST_START_FROM_1 | IME_PROP_UNICODE | IME_PROP_AT_CARET | 0xa,
|
|
.fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE,
|
|
.fdwSentenceCaps = IME_SMODE_PLAURALCLAUSE,
|
|
.fdwSCSCaps = SCS_CAP_COMPSTR | SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD,
|
|
.fdwUICaps = UI_CAP_ROT90,
|
|
};
|
|
HKL hkl = GetKeyboardLayout( 0 );
|
|
const IMEINFO *expect;
|
|
|
|
SET_ENABLE( ImeInquire, TRUE );
|
|
SET_ENABLE( ImeDestroy, TRUE );
|
|
|
|
SetLastError( 0xdeadbeef );
|
|
ok_ret( 0, ImmGetProperty( 0, 0 ) );
|
|
ok_ret( 0, ImmGetProperty( hkl, 0 ) );
|
|
|
|
if (hkl == (HKL)0x04110411) expect = &expect_ime_info_ja_JP;
|
|
else if (hkl == (HKL)0x04120412) expect = &expect_ime_info_ko_KR;
|
|
else if (hkl == (HKL)0x08040804) expect = &expect_ime_info_zh_CN;
|
|
else expect = &expect_ime_info;
|
|
|
|
/* IME_PROP_COMPLETE_ON_UNSELECT seems to be sometimes set on CJK locales IMEs, sometimes not */
|
|
todo_wine_if( expect != &expect_ime_info )
|
|
ok_ret( expect->fdwProperty, ImmGetProperty( hkl, IGP_PROPERTY ) & ~IME_PROP_COMPLETE_ON_UNSELECT );
|
|
todo_wine_if( expect != &expect_ime_info_zh_CN && expect != &expect_ime_info_ko_KR )
|
|
ok_ret( expect->fdwConversionCaps, ImmGetProperty( hkl, IGP_CONVERSION ) );
|
|
todo_wine
|
|
ok_ret( expect->fdwSentenceCaps, ImmGetProperty( hkl, IGP_SENTENCE ) );
|
|
todo_wine_if( expect != &expect_ime_info )
|
|
ok_ret( expect->fdwSCSCaps, ImmGetProperty( hkl, IGP_SETCOMPSTR ) );
|
|
todo_wine_if( expect != &expect_ime_info_ko_KR )
|
|
ok_ret( expect->fdwSelectCaps, ImmGetProperty( hkl, IGP_SELECT ) );
|
|
ok_ret( IMEVER_0400, ImmGetProperty( hkl, IGP_GETIMEVERSION ) );
|
|
todo_wine_if( expect != &expect_ime_info )
|
|
ok_ret( expect->fdwUICaps, ImmGetProperty( hkl, IGP_UI ) );
|
|
todo_wine
|
|
ok_ret( 0xdeadbeef, GetLastError() );
|
|
|
|
/* IME_PROP_END_UNLOAD for the IME to unload / reload. */
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD;
|
|
|
|
if (!(hkl = wineime_hkl)) goto cleanup;
|
|
|
|
SET_EXPECT( ImeInquire );
|
|
SET_EXPECT( ImeDestroy );
|
|
ok_ret( 0, ImmGetProperty( hkl, 0 ) );
|
|
CHECK_CALLED( ImeInquire );
|
|
CHECK_CALLED( ImeDestroy );
|
|
|
|
expect = &ime_info;
|
|
todo_ImeInquire = TRUE;
|
|
todo_ImeDestroy = TRUE;
|
|
ok_ret( expect->fdwProperty, ImmGetProperty( hkl, IGP_PROPERTY ) );
|
|
ok_ret( expect->fdwConversionCaps, ImmGetProperty( hkl, IGP_CONVERSION ) );
|
|
ok_ret( expect->fdwSentenceCaps, ImmGetProperty( hkl, IGP_SENTENCE ) );
|
|
ok_ret( expect->fdwSCSCaps, ImmGetProperty( hkl, IGP_SETCOMPSTR ) );
|
|
ok_ret( expect->fdwSelectCaps, ImmGetProperty( hkl, IGP_SELECT ) );
|
|
ok_ret( IMEVER_0400, ImmGetProperty( hkl, IGP_GETIMEVERSION ) );
|
|
ok_ret( expect->fdwUICaps, ImmGetProperty( hkl, IGP_UI ) );
|
|
todo_ImeInquire = FALSE;
|
|
called_ImeInquire = FALSE;
|
|
todo_ImeDestroy = FALSE;
|
|
called_ImeDestroy = FALSE;
|
|
|
|
cleanup:
|
|
SET_ENABLE( ImeInquire, FALSE );
|
|
SET_ENABLE( ImeDestroy, FALSE );
|
|
}
|
|
|
|
static void test_ImmGetDescription(void)
|
|
{
|
|
HKL hkl = GetKeyboardLayout( 0 );
|
|
WCHAR bufferW[MAX_PATH];
|
|
char bufferA[MAX_PATH];
|
|
DWORD ret;
|
|
|
|
SET_ENABLE( IME_DLL_PROCESS_ATTACH, TRUE );
|
|
SET_ENABLE( ImeInquire, TRUE );
|
|
SET_ENABLE( ImeDestroy, TRUE );
|
|
SET_ENABLE( IME_DLL_PROCESS_DETACH, TRUE );
|
|
|
|
SetLastError( 0xdeadbeef );
|
|
ret = ImmGetDescriptionW( NULL, NULL, 0 );
|
|
ok( !ret, "ImmGetDescriptionW returned %lu\n", ret );
|
|
ret = ImmGetDescriptionA( NULL, NULL, 0 );
|
|
ok( !ret, "ImmGetDescriptionA returned %lu\n", ret );
|
|
ret = ImmGetDescriptionW( NULL, NULL, 100 );
|
|
ok( !ret, "ImmGetDescriptionW returned %lu\n", ret );
|
|
ret = ImmGetDescriptionA( NULL, NULL, 100 );
|
|
ok( !ret, "ImmGetDescriptionA returned %lu\n", ret );
|
|
ret = ImmGetDescriptionW( hkl, bufferW, 100 );
|
|
ok( !ret, "ImmGetDescriptionW returned %lu\n", ret );
|
|
ret = ImmGetDescriptionA( hkl, bufferA, 100 );
|
|
ok( !ret, "ImmGetDescriptionA returned %lu\n", ret );
|
|
ret = GetLastError();
|
|
ok( ret == 0xdeadbeef, "got error %lu\n", ret );
|
|
|
|
if (!(hkl = wineime_hkl)) goto cleanup;
|
|
|
|
memset( bufferW, 0xcd, sizeof(bufferW) );
|
|
ret = ImmGetDescriptionW( hkl, bufferW, 2 );
|
|
ok( ret == 1, "ImmGetDescriptionW returned %lu\n", ret );
|
|
ok( !wcscmp( bufferW, L"W" ), "got bufferW %s\n", debugstr_w(bufferW) );
|
|
memset( bufferA, 0xcd, sizeof(bufferA) );
|
|
ret = ImmGetDescriptionA( hkl, bufferA, 2 );
|
|
ok( ret == 0, "ImmGetDescriptionA returned %lu\n", ret );
|
|
ok( !strcmp( bufferA, "" ), "got bufferA %s\n", debugstr_a(bufferA) );
|
|
|
|
memset( bufferW, 0xcd, sizeof(bufferW) );
|
|
ret = ImmGetDescriptionW( hkl, bufferW, 11 );
|
|
ok( ret == 10, "ImmGetDescriptionW returned %lu\n", ret );
|
|
ok( !wcscmp( bufferW, L"WineTest I" ), "got bufferW %s\n", debugstr_w(bufferW) );
|
|
memset( bufferA, 0xcd, sizeof(bufferA) );
|
|
ret = ImmGetDescriptionA( hkl, bufferA, 11 );
|
|
ok( ret == 0, "ImmGetDescriptionA returned %lu\n", ret );
|
|
ok( !strcmp( bufferA, "" ), "got bufferA %s\n", debugstr_a(bufferA) );
|
|
|
|
memset( bufferW, 0xcd, sizeof(bufferW) );
|
|
ret = ImmGetDescriptionW( hkl, bufferW, 12 );
|
|
ok( ret == 11, "ImmGetDescriptionW returned %lu\n", ret );
|
|
ok( !wcscmp( bufferW, L"WineTest IM" ), "got bufferW %s\n", debugstr_w(bufferW) );
|
|
memset( bufferA, 0xcd, sizeof(bufferA) );
|
|
ret = ImmGetDescriptionA( hkl, bufferA, 12 );
|
|
ok( ret == 12, "ImmGetDescriptionA returned %lu\n", ret );
|
|
ok( !strcmp( bufferA, "WineTest IME" ), "got bufferA %s\n", debugstr_a(bufferA) );
|
|
|
|
memset( bufferW, 0xcd, sizeof(bufferW) );
|
|
ret = ImmGetDescriptionW( hkl, bufferW, 13 );
|
|
ok( ret == 12, "ImmGetDescriptionW returned %lu\n", ret );
|
|
ok( !wcscmp( bufferW, L"WineTest IME" ), "got bufferW %s\n", debugstr_w(bufferW) );
|
|
memset( bufferA, 0xcd, sizeof(bufferA) );
|
|
ret = ImmGetDescriptionA( hkl, bufferA, 13 );
|
|
ok( ret == 12, "ImmGetDescriptionA returned %lu\n", ret );
|
|
ok( !strcmp( bufferA, "WineTest IME" ), "got bufferA %s\n", debugstr_a(bufferA) );
|
|
|
|
cleanup:
|
|
SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE );
|
|
SET_ENABLE( ImeInquire, FALSE );
|
|
SET_ENABLE( ImeDestroy, FALSE );
|
|
SET_ENABLE( IME_DLL_PROCESS_DETACH, FALSE );
|
|
}
|
|
|
|
static void test_ImmGetIMEFileName(void)
|
|
{
|
|
HKL hkl = GetKeyboardLayout( 0 );
|
|
WCHAR bufferW[MAX_PATH], expectW[16];
|
|
char bufferA[MAX_PATH], expectA[16];
|
|
DWORD ret;
|
|
|
|
SET_ENABLE( IME_DLL_PROCESS_ATTACH, TRUE );
|
|
SET_ENABLE( ImeInquire, TRUE );
|
|
SET_ENABLE( ImeDestroy, TRUE );
|
|
SET_ENABLE( IME_DLL_PROCESS_DETACH, TRUE );
|
|
|
|
SetLastError( 0xdeadbeef );
|
|
ret = ImmGetIMEFileNameW( NULL, NULL, 0 );
|
|
ok( !ret, "ImmGetIMEFileNameW returned %lu\n", ret );
|
|
ret = ImmGetIMEFileNameA( NULL, NULL, 0 );
|
|
ok( !ret, "ImmGetIMEFileNameA returned %lu\n", ret );
|
|
ret = ImmGetIMEFileNameW( NULL, NULL, 100 );
|
|
ok( !ret, "ImmGetIMEFileNameW returned %lu\n", ret );
|
|
ret = ImmGetIMEFileNameA( NULL, NULL, 100 );
|
|
ok( !ret, "ImmGetIMEFileNameA returned %lu\n", ret );
|
|
ret = ImmGetIMEFileNameW( hkl, bufferW, 100 );
|
|
ok( !ret, "ImmGetIMEFileNameW returned %lu\n", ret );
|
|
ret = ImmGetIMEFileNameA( hkl, bufferA, 100 );
|
|
ok( !ret, "ImmGetIMEFileNameA returned %lu\n", ret );
|
|
ret = GetLastError();
|
|
ok( ret == 0xdeadbeef, "got error %lu\n", ret );
|
|
|
|
if (!(hkl = wineime_hkl)) goto cleanup;
|
|
|
|
memset( bufferW, 0xcd, sizeof(bufferW) );
|
|
ret = ImmGetIMEFileNameW( hkl, bufferW, 2 );
|
|
ok( ret == 1, "ImmGetIMEFileNameW returned %lu\n", ret );
|
|
ok( !wcscmp( bufferW, L"W" ), "got bufferW %s\n", debugstr_w(bufferW) );
|
|
memset( bufferA, 0xcd, sizeof(bufferA) );
|
|
ret = ImmGetIMEFileNameA( hkl, bufferA, 2 );
|
|
ok( ret == 0, "ImmGetIMEFileNameA returned %lu\n", ret );
|
|
ok( !strcmp( bufferA, "" ), "got bufferA %s\n", debugstr_a(bufferA) );
|
|
|
|
swprintf( expectW, ARRAY_SIZE(expectW), L"WINE%04X.I", ime_count - 1 );
|
|
memset( bufferW, 0xcd, sizeof(bufferW) );
|
|
ret = ImmGetIMEFileNameW( hkl, bufferW, 11 );
|
|
ok( ret == 10, "ImmGetIMEFileNameW returned %lu\n", ret );
|
|
ok( !wcscmp( bufferW, expectW ), "got bufferW %s\n", debugstr_w(bufferW) );
|
|
memset( bufferA, 0xcd, sizeof(bufferA) );
|
|
ret = ImmGetIMEFileNameA( hkl, bufferA, 11 );
|
|
ok( ret == 0, "ImmGetIMEFileNameA returned %lu\n", ret );
|
|
ok( !strcmp( bufferA, "" ), "got bufferA %s\n", debugstr_a(bufferA) );
|
|
|
|
swprintf( expectW, ARRAY_SIZE(expectW), L"WINE%04X.IM", ime_count - 1 );
|
|
memset( bufferW, 0xcd, sizeof(bufferW) );
|
|
ret = ImmGetIMEFileNameW( hkl, bufferW, 12 );
|
|
ok( ret == 11, "ImmGetIMEFileNameW returned %lu\n", ret );
|
|
ok( !wcscmp( bufferW, expectW ), "got bufferW %s\n", debugstr_w(bufferW) );
|
|
snprintf( expectA, ARRAY_SIZE(expectA), "WINE%04X.IME", ime_count - 1 );
|
|
memset( bufferA, 0xcd, sizeof(bufferA) );
|
|
ret = ImmGetIMEFileNameA( hkl, bufferA, 12 );
|
|
ok( ret == 12, "ImmGetIMEFileNameA returned %lu\n", ret );
|
|
ok( !strcmp( bufferA, expectA ), "got bufferA %s\n", debugstr_a(bufferA) );
|
|
|
|
swprintf( expectW, ARRAY_SIZE(expectW), L"WINE%04X.IME", ime_count - 1 );
|
|
memset( bufferW, 0xcd, sizeof(bufferW) );
|
|
ret = ImmGetIMEFileNameW( hkl, bufferW, 13 );
|
|
ok( ret == 12, "ImmGetIMEFileNameW returned %lu\n", ret );
|
|
ok( !wcscmp( bufferW, expectW ), "got bufferW %s\n", debugstr_w(bufferW) );
|
|
memset( bufferA, 0xcd, sizeof(bufferA) );
|
|
ret = ImmGetIMEFileNameA( hkl, bufferA, 13 );
|
|
ok( ret == 12, "ImmGetIMEFileNameA returned %lu\n", ret );
|
|
ok( !strcmp( bufferA, expectA ), "got bufferA %s\n", debugstr_a(bufferA) );
|
|
|
|
cleanup:
|
|
SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE );
|
|
SET_ENABLE( ImeInquire, FALSE );
|
|
SET_ENABLE( ImeDestroy, FALSE );
|
|
SET_ENABLE( IME_DLL_PROCESS_DETACH, FALSE );
|
|
}
|
|
|
|
static void test_ImmEscape( BOOL unicode )
|
|
{
|
|
HKL hkl = GetKeyboardLayout( 0 );
|
|
DWORD i, codes[] =
|
|
{
|
|
IME_ESC_QUERY_SUPPORT,
|
|
IME_ESC_SEQUENCE_TO_INTERNAL,
|
|
IME_ESC_GET_EUDC_DICTIONARY,
|
|
IME_ESC_SET_EUDC_DICTIONARY,
|
|
IME_ESC_MAX_KEY,
|
|
IME_ESC_IME_NAME,
|
|
IME_ESC_HANJA_MODE,
|
|
IME_ESC_GETHELPFILENAME,
|
|
};
|
|
WCHAR bufferW[512];
|
|
char bufferA[512];
|
|
|
|
SET_ENABLE( ImeEscape, TRUE );
|
|
|
|
winetest_push_context( unicode ? "unicode" : "ansi" );
|
|
|
|
ok_ret( 0, ImmEscapeW( hkl, 0, 0, NULL ) );
|
|
ok_ret( 0, ImmEscapeA( hkl, 0, 0, NULL ) );
|
|
|
|
/* IME_PROP_END_UNLOAD for the IME to unload / reload. */
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD;
|
|
if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
|
|
|
|
if (!(hkl = wineime_hkl)) goto cleanup;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(codes); ++i)
|
|
{
|
|
winetest_push_context( "esc %#lx", codes[i] );
|
|
|
|
SET_EXPECT( ImeEscape );
|
|
ok_ret( 4, ImmEscapeW( hkl, 0, codes[i], NULL ) );
|
|
CHECK_CALLED( ImeEscape );
|
|
|
|
SET_EXPECT( ImeEscape );
|
|
memset( bufferW, 0xcd, sizeof(bufferW) );
|
|
if (codes[i] == IME_ESC_SET_EUDC_DICTIONARY) wcscpy( bufferW, L"EscapeIme" );
|
|
ok_ret( 4, ImmEscapeW( hkl, 0, codes[i], bufferW ) );
|
|
if (unicode || codes[i] == IME_ESC_GET_EUDC_DICTIONARY || codes[i] == IME_ESC_IME_NAME ||
|
|
codes[i] == IME_ESC_GETHELPFILENAME)
|
|
{
|
|
ok_wcs( L"ImeEscape", bufferW );
|
|
ok_eq( 0xcdcd, bufferW[10], WORD, "%#x" );
|
|
}
|
|
else if (codes[i] == IME_ESC_SET_EUDC_DICTIONARY)
|
|
{
|
|
ok_wcs( L"EscapeIme", bufferW );
|
|
ok_eq( 0xcdcd, bufferW[10], WORD, "%#x" );
|
|
}
|
|
else if (codes[i] == IME_ESC_HANJA_MODE)
|
|
{
|
|
todo_wine
|
|
ok_eq( 0xcdcd, bufferW[0], WORD, "%#x" );
|
|
}
|
|
else
|
|
{
|
|
ok( !memcmp( bufferW, "ImeEscape", 10 ), "got bufferW %s\n", debugstr_w(bufferW) );
|
|
ok_eq( 0xcdcd, bufferW[5], WORD, "%#x" );
|
|
}
|
|
CHECK_CALLED( ImeEscape );
|
|
|
|
SET_EXPECT( ImeEscape );
|
|
ok_ret( 4, ImmEscapeA( hkl, 0, codes[i], NULL ) );
|
|
CHECK_CALLED( ImeEscape );
|
|
|
|
SET_EXPECT( ImeEscape );
|
|
memset( bufferA, 0xcd, sizeof(bufferA) );
|
|
if (codes[i] == IME_ESC_SET_EUDC_DICTIONARY) strcpy( bufferA, "EscapeIme" );
|
|
ok_ret( 4, ImmEscapeA( hkl, 0, codes[i], bufferA ) );
|
|
if (!unicode || codes[i] == IME_ESC_GET_EUDC_DICTIONARY || codes[i] == IME_ESC_IME_NAME ||
|
|
codes[i] == IME_ESC_GETHELPFILENAME)
|
|
{
|
|
ok_str( "ImeEscape", bufferA );
|
|
ok_eq( 0xcd, bufferA[10], BYTE, "%#x" );
|
|
}
|
|
else if (codes[i] == IME_ESC_SET_EUDC_DICTIONARY)
|
|
{
|
|
ok_str( "EscapeIme", bufferA );
|
|
ok_eq( 0xcd, bufferA[10], BYTE, "%#x" );
|
|
}
|
|
else if (codes[i] == IME_ESC_HANJA_MODE)
|
|
{
|
|
todo_wine
|
|
ok_eq( 0xcd, bufferA[0], BYTE, "%#x" );
|
|
}
|
|
else
|
|
{
|
|
ok( !memcmp( bufferA, L"ImeEscape", 10 * sizeof(WCHAR) ), "got bufferA %s\n", debugstr_a(bufferA) );
|
|
ok_eq( 0xcd, bufferA[20], BYTE, "%#x" );
|
|
}
|
|
CHECK_CALLED( ImeEscape );
|
|
|
|
winetest_pop_context();
|
|
}
|
|
|
|
cleanup:
|
|
SET_ENABLE( ImeEscape, FALSE );
|
|
|
|
winetest_pop_context();
|
|
}
|
|
|
|
static int CALLBACK enum_register_wordA( const char *reading, DWORD style, const char *string, void *user )
|
|
{
|
|
ime_trace( "reading %s, style %#lx, string %s, user %p\n", debugstr_a(reading), style, debugstr_a(string), user );
|
|
|
|
ok_eq( 0xdeadbeef, style, UINT, "%#x" );
|
|
ok_str( "Reading", reading );
|
|
ok_str( "String", string );
|
|
|
|
return 0xdeadbeef;
|
|
}
|
|
|
|
static int CALLBACK enum_register_wordW( const WCHAR *reading, DWORD style, const WCHAR *string, void *user )
|
|
{
|
|
ime_trace( "reading %s, style %#lx, string %s, user %p\n", debugstr_w(reading), style, debugstr_w(string), user );
|
|
|
|
ok_eq( 0xdeadbeef, style, UINT, "%#x" );
|
|
ok_wcs( L"Reading", reading );
|
|
ok_wcs( L"String", string );
|
|
|
|
return 0xdeadbeef;
|
|
}
|
|
|
|
static void test_ImmEnumRegisterWord( BOOL unicode )
|
|
{
|
|
HKL hkl = GetKeyboardLayout( 0 );
|
|
|
|
winetest_push_context( unicode ? "unicode" : "ansi" );
|
|
|
|
SET_ENABLE( ImeEnumRegisterWord, TRUE );
|
|
|
|
SetLastError( 0xdeadbeef );
|
|
ok_ret( 0, ImmEnumRegisterWordW( NULL, enum_register_wordW, NULL, 0, NULL, NULL ) );
|
|
ok_ret( 0, ImmEnumRegisterWordA( NULL, enum_register_wordA, NULL, 0, NULL, NULL ) );
|
|
ok_ret( 0, ImmEnumRegisterWordW( hkl, enum_register_wordW, NULL, 0, NULL, NULL ) );
|
|
ok_ret( 0, ImmEnumRegisterWordA( hkl, enum_register_wordA, NULL, 0, NULL, NULL ) );
|
|
todo_wine
|
|
ok_ret( 0xdeadbeef, GetLastError() );
|
|
|
|
/* IME_PROP_END_UNLOAD for the IME to unload / reload. */
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD;
|
|
if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
|
|
|
|
if (!(hkl = wineime_hkl)) goto cleanup;
|
|
|
|
SET_EXPECT( ImeEnumRegisterWord );
|
|
ok_ret( 0, ImmEnumRegisterWordW( hkl, enum_register_wordW, NULL, 0, NULL, NULL ) );
|
|
CHECK_CALLED( ImeEnumRegisterWord );
|
|
|
|
SET_EXPECT( ImeEnumRegisterWord );
|
|
ok_ret( 0, ImmEnumRegisterWordA( hkl, enum_register_wordA, NULL, 0, NULL, NULL ) );
|
|
CHECK_CALLED( ImeEnumRegisterWord );
|
|
|
|
SET_EXPECT( ImeEnumRegisterWord );
|
|
ok_ret( 0xdeadbeef, ImmEnumRegisterWordW( hkl, enum_register_wordW, L"Reading", 0xdeadbeef, L"String", NULL ) );
|
|
CHECK_CALLED( ImeEnumRegisterWord );
|
|
|
|
SET_EXPECT( ImeEnumRegisterWord );
|
|
ok_ret( 0xdeadbeef, ImmEnumRegisterWordA( hkl, enum_register_wordA, "Reading", 0xdeadbeef, "String", NULL ) );
|
|
CHECK_CALLED( ImeEnumRegisterWord );
|
|
|
|
cleanup:
|
|
SET_ENABLE( ImeEnumRegisterWord, FALSE );
|
|
|
|
winetest_pop_context();
|
|
}
|
|
|
|
static void test_ImmRegisterWord( BOOL unicode )
|
|
{
|
|
HKL hkl = GetKeyboardLayout( 0 );
|
|
|
|
SET_ENABLE( ImeRegisterWord, TRUE );
|
|
|
|
winetest_push_context( unicode ? "unicode" : "ansi" );
|
|
|
|
SetLastError( 0xdeadbeef );
|
|
ok_ret( 0, ImmRegisterWordW( NULL, NULL, 0, NULL ) );
|
|
ok_ret( 0, ImmRegisterWordA( NULL, NULL, 0, NULL ) );
|
|
ok_ret( 0, ImmRegisterWordW( hkl, NULL, 0, NULL ) );
|
|
ok_ret( 0, ImmRegisterWordA( hkl, NULL, 0, NULL ) );
|
|
todo_wine
|
|
ok_ret( 0xdeadbeef, GetLastError() );
|
|
|
|
/* IME_PROP_END_UNLOAD for the IME to unload / reload. */
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD;
|
|
if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
|
|
|
|
if (!(hkl = wineime_hkl)) goto cleanup;
|
|
|
|
SET_EXPECT( ImeRegisterWord );
|
|
ok_ret( 0, ImmRegisterWordW( hkl, NULL, 0, NULL ) );
|
|
CHECK_CALLED( ImeRegisterWord );
|
|
|
|
SET_EXPECT( ImeRegisterWord );
|
|
ok_ret( 0, ImmRegisterWordA( hkl, NULL, 0, NULL ) );
|
|
CHECK_CALLED( ImeRegisterWord );
|
|
|
|
SET_EXPECT( ImeRegisterWord );
|
|
ok_ret( 0, ImmRegisterWordW( hkl, L"Reading", 0, NULL ) );
|
|
CHECK_CALLED( ImeRegisterWord );
|
|
|
|
SET_EXPECT( ImeRegisterWord );
|
|
ok_ret( 0, ImmRegisterWordA( hkl, "Reading", 0, NULL ) );
|
|
CHECK_CALLED( ImeRegisterWord );
|
|
|
|
SET_EXPECT( ImeRegisterWord );
|
|
ok_ret( 0, ImmRegisterWordW( hkl, NULL, 0xdeadbeef, NULL ) );
|
|
CHECK_CALLED( ImeRegisterWord );
|
|
|
|
SET_EXPECT( ImeRegisterWord );
|
|
ok_ret( 0, ImmRegisterWordA( hkl, NULL, 0xdeadbeef, NULL ) );
|
|
CHECK_CALLED( ImeRegisterWord );
|
|
|
|
SET_EXPECT( ImeRegisterWord );
|
|
ok_ret( 0, ImmRegisterWordW( hkl, NULL, 0, L"String" ) );
|
|
CHECK_CALLED( ImeRegisterWord );
|
|
|
|
SET_EXPECT( ImeRegisterWord );
|
|
ok_ret( 0, ImmRegisterWordA( hkl, NULL, 0, "String" ) );
|
|
CHECK_CALLED( ImeRegisterWord );
|
|
|
|
cleanup:
|
|
SET_ENABLE( ImeRegisterWord, FALSE );
|
|
|
|
winetest_pop_context();
|
|
}
|
|
|
|
static void test_ImmGetRegisterWordStyle( BOOL unicode )
|
|
{
|
|
HKL hkl = GetKeyboardLayout( 0 );
|
|
STYLEBUFW styleW;
|
|
STYLEBUFA styleA;
|
|
|
|
winetest_push_context( unicode ? "unicode" : "ansi" );
|
|
|
|
SET_ENABLE( ImeGetRegisterWordStyle, TRUE );
|
|
|
|
SetLastError( 0xdeadbeef );
|
|
ok_ret( 0, ImmGetRegisterWordStyleW( NULL, 0, &styleW ) );
|
|
ok_ret( 0, ImmGetRegisterWordStyleA( NULL, 0, &styleA ) );
|
|
ok_ret( 0, ImmGetRegisterWordStyleW( hkl, 0, &styleW ) );
|
|
ok_ret( 0, ImmGetRegisterWordStyleA( hkl, 0, &styleA ) );
|
|
todo_wine
|
|
ok_ret( 0xdeadbeef, GetLastError() );
|
|
|
|
/* IME_PROP_END_UNLOAD for the IME to unload / reload. */
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD;
|
|
if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
|
|
|
|
if (!(hkl = wineime_hkl)) goto cleanup;
|
|
|
|
if (!strcmp( winetest_platform, "wine" )) goto skip_null;
|
|
|
|
SET_EXPECT( ImeGetRegisterWordStyle );
|
|
ok_ret( 0xdeadbeef, ImmGetRegisterWordStyleW( hkl, 16, NULL ) );
|
|
CHECK_CALLED( ImeGetRegisterWordStyle );
|
|
|
|
SET_EXPECT( ImeGetRegisterWordStyle );
|
|
ok_ret( 0xdeadbeef, ImmGetRegisterWordStyleA( hkl, 16, NULL ) );
|
|
CHECK_CALLED( ImeGetRegisterWordStyle );
|
|
|
|
skip_null:
|
|
SET_EXPECT( ImeGetRegisterWordStyle );
|
|
memset( &styleW, 0xcd, sizeof(styleW) );
|
|
ok_ret( 0xdeadbeef, ImmGetRegisterWordStyleW( hkl, 1, &styleW ) );
|
|
if (ime_info.fdwProperty & IME_PROP_UNICODE)
|
|
{
|
|
ok_eq( 0xdeadbeef, styleW.dwStyle, UINT, "%#x" );
|
|
ok_wcs( L"StyleDescription", styleW.szDescription );
|
|
}
|
|
else
|
|
{
|
|
todo_wine
|
|
ok_eq( 0xcdcdcdcd, styleW.dwStyle, UINT, "%#x" );
|
|
todo_wine
|
|
ok_eq( 0xcdcd, styleW.szDescription[0], WORD, "%#x" );
|
|
}
|
|
CHECK_CALLED( ImeGetRegisterWordStyle );
|
|
|
|
SET_EXPECT( ImeGetRegisterWordStyle );
|
|
memset( &styleA, 0xcd, sizeof(styleA) );
|
|
ok_ret( 0xdeadbeef, ImmGetRegisterWordStyleA( hkl, 1, &styleA ) );
|
|
if (ime_info.fdwProperty & IME_PROP_UNICODE)
|
|
{
|
|
todo_wine
|
|
ok_eq( 0xcdcdcdcd, styleA.dwStyle, UINT, "%#x" );
|
|
todo_wine
|
|
ok_eq( 0xcd, styleA.szDescription[0], BYTE, "%#x" );
|
|
}
|
|
else
|
|
{
|
|
ok_eq( 0xdeadbeef, styleA.dwStyle, UINT, "%#x" );
|
|
ok_str( "StyleDescription", styleA.szDescription );
|
|
}
|
|
CHECK_CALLED( ImeGetRegisterWordStyle );
|
|
|
|
cleanup:
|
|
SET_ENABLE( ImeGetRegisterWordStyle, FALSE );
|
|
|
|
winetest_pop_context();
|
|
}
|
|
|
|
static void test_ImmUnregisterWord( BOOL unicode )
|
|
{
|
|
HKL hkl = GetKeyboardLayout( 0 );
|
|
|
|
winetest_push_context( unicode ? "unicode" : "ansi" );
|
|
|
|
SET_ENABLE( ImeUnregisterWord, TRUE );
|
|
|
|
SetLastError( 0xdeadbeef );
|
|
ok_ret( 0, ImmUnregisterWordW( NULL, NULL, 0, NULL ) );
|
|
ok_ret( 0, ImmUnregisterWordA( NULL, NULL, 0, NULL ) );
|
|
ok_ret( 0, ImmUnregisterWordW( hkl, NULL, 0, NULL ) );
|
|
ok_ret( 0, ImmUnregisterWordA( hkl, NULL, 0, NULL ) );
|
|
todo_wine
|
|
ok_ret( 0xdeadbeef, GetLastError() );
|
|
|
|
/* IME_PROP_END_UNLOAD for the IME to unload / reload. */
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD;
|
|
if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
|
|
|
|
if (!(hkl = wineime_hkl)) goto cleanup;
|
|
|
|
SET_EXPECT( ImeUnregisterWord );
|
|
ok_ret( 0, ImmUnregisterWordW( hkl, NULL, 0, NULL ) );
|
|
CHECK_CALLED( ImeUnregisterWord );
|
|
|
|
SET_EXPECT( ImeUnregisterWord );
|
|
ok_ret( 0, ImmUnregisterWordA( hkl, NULL, 0, NULL ) );
|
|
CHECK_CALLED( ImeUnregisterWord );
|
|
|
|
SET_EXPECT( ImeUnregisterWord );
|
|
ok_ret( 0, ImmUnregisterWordW( hkl, L"Reading", 0, NULL ) );
|
|
CHECK_CALLED( ImeUnregisterWord );
|
|
|
|
SET_EXPECT( ImeUnregisterWord );
|
|
ok_ret( 0, ImmUnregisterWordA( hkl, "Reading", 0, NULL ) );
|
|
CHECK_CALLED( ImeUnregisterWord );
|
|
|
|
SET_EXPECT( ImeUnregisterWord );
|
|
ok_ret( 0, ImmUnregisterWordW( hkl, NULL, 0xdeadbeef, NULL ) );
|
|
CHECK_CALLED( ImeUnregisterWord );
|
|
|
|
SET_EXPECT( ImeUnregisterWord );
|
|
ok_ret( 0, ImmUnregisterWordA( hkl, NULL, 0xdeadbeef, NULL ) );
|
|
CHECK_CALLED( ImeUnregisterWord );
|
|
|
|
SET_EXPECT( ImeUnregisterWord );
|
|
ok_ret( 0, ImmUnregisterWordW( hkl, NULL, 0, L"String" ) );
|
|
CHECK_CALLED( ImeUnregisterWord );
|
|
|
|
SET_EXPECT( ImeUnregisterWord );
|
|
ok_ret( 0, ImmUnregisterWordA( hkl, NULL, 0, "String" ) );
|
|
CHECK_CALLED( ImeUnregisterWord );
|
|
|
|
cleanup:
|
|
SET_ENABLE( ImeUnregisterWord, FALSE );
|
|
|
|
winetest_pop_context();
|
|
}
|
|
|
|
struct ime_windows
|
|
{
|
|
HWND ime_hwnd;
|
|
HWND ime_ui_hwnd;
|
|
};
|
|
|
|
static BOOL CALLBACK enum_thread_ime_windows( HWND hwnd, LPARAM lparam )
|
|
{
|
|
struct ime_windows *params = (void *)lparam;
|
|
WCHAR buffer[256];
|
|
UINT ret;
|
|
|
|
ime_trace( "hwnd %p, lparam %#Ix\n", hwnd, lparam );
|
|
|
|
ret = RealGetWindowClassW( hwnd, buffer, ARRAY_SIZE(buffer) );
|
|
ok( ret, "RealGetWindowClassW returned %#x\n", ret );
|
|
|
|
if (!wcscmp( buffer, L"IME" ))
|
|
{
|
|
ok( !params->ime_hwnd, "Found extra IME window %p\n", hwnd );
|
|
params->ime_hwnd = hwnd;
|
|
}
|
|
if (!wcscmp( buffer, ime_ui_class.lpszClassName ))
|
|
{
|
|
ok( !params->ime_ui_hwnd, "Found extra IME UI window %p\n", hwnd );
|
|
params->ime_ui_hwnd = hwnd;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void test_ImmSetConversionStatus(void)
|
|
{
|
|
const struct ime_call set_conversion_status_0_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCONVERSIONMODE},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETSENTENCEMODE},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSENTENCEMODE},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSENTENCEMODE},
|
|
},
|
|
{0},
|
|
};
|
|
const struct ime_call set_conversion_status_1_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0xdeadbeef, .value = IMC_SETCONVERSIONMODE},
|
|
},
|
|
{0},
|
|
};
|
|
const struct ime_call set_conversion_status_2_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCONVERSIONMODE},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0xfeedcafe, .value = IMC_SETSENTENCEMODE},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSENTENCEMODE},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSENTENCEMODE},
|
|
},
|
|
{0},
|
|
};
|
|
DWORD old_conversion, old_sentence, conversion, sentence;
|
|
HKL hkl;
|
|
INPUTCONTEXT *ctx;
|
|
HWND hwnd;
|
|
|
|
ok_ret( 0, ImmGetConversionStatus( 0, &old_conversion, &old_sentence ) );
|
|
ok_ret( 1, ImmGetConversionStatus( default_himc, &old_conversion, &old_sentence ) );
|
|
|
|
ctx = ImmLockIMC( default_himc );
|
|
ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
|
|
ok_eq( old_conversion, ctx->fdwConversion, UINT, "%#x" );
|
|
ok_eq( old_sentence, ctx->fdwSentence, UINT, "%#x" );
|
|
|
|
hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
process_messages();
|
|
|
|
ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) );
|
|
ok_eq( old_conversion, conversion, UINT, "%#x" );
|
|
ok_eq( old_sentence, sentence, UINT, "%#x" );
|
|
ok_eq( old_conversion, ctx->fdwConversion, UINT, "%#x" );
|
|
ok_eq( old_sentence, ctx->fdwSentence, UINT, "%#x" );
|
|
|
|
ok_ret( 1, ImmSetConversionStatus( default_himc, 0, 0 ) );
|
|
ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) );
|
|
ok_eq( 0, conversion, UINT, "%#x" );
|
|
ok_eq( 0, sentence, UINT, "%#x" );
|
|
ok_eq( 0, ctx->fdwConversion, UINT, "%#x" );
|
|
ok_eq( 0, ctx->fdwSentence, UINT, "%#x" );
|
|
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
|
|
|
|
if (!(hkl = wineime_hkl)) goto cleanup;
|
|
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
ok_ret( 1, ImmLoadIME( hkl ) );
|
|
process_messages();
|
|
/* initial values are dependent on both old and new IME */
|
|
ok_ret( 1, ImmSetConversionStatus( default_himc, 0, 0 ) );
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) );
|
|
ok_eq( 0, conversion, UINT, "%#x" );
|
|
ok_eq( 0, sentence, UINT, "%#x" );
|
|
ok_eq( 0, ctx->fdwConversion, UINT, "%#x" );
|
|
ok_eq( 0, ctx->fdwSentence, UINT, "%#x" );
|
|
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 1, ImmSetConversionStatus( default_himc, 0xdeadbeef, 0xfeedcafe ) );
|
|
ok_seq( set_conversion_status_0_seq );
|
|
|
|
ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) );
|
|
ok_eq( 0xdeadbeef, conversion, UINT, "%#x" );
|
|
ok_eq( 0xfeedcafe, sentence, UINT, "%#x" );
|
|
ok_eq( 0xdeadbeef, ctx->fdwConversion, UINT, "%#x" );
|
|
ok_eq( 0xfeedcafe, ctx->fdwSentence, UINT, "%#x" );
|
|
|
|
ok_ret( 1, ImmSetConversionStatus( default_himc, 0xdeadbeef, 0xfeedcafe ) );
|
|
ok_seq( empty_sequence );
|
|
|
|
ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, NULL ) );
|
|
ok_eq( 0xdeadbeef, conversion, UINT, "%#x" );
|
|
ok_eq( 0xdeadbeef, ctx->fdwConversion, UINT, "%#x" );
|
|
ok_eq( 0xfeedcafe, ctx->fdwSentence, UINT, "%#x" );
|
|
|
|
ctx->hWnd = 0;
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 1, ImmSetConversionStatus( default_himc, 0, 0xfeedcafe ) );
|
|
ok_seq( set_conversion_status_1_seq );
|
|
|
|
ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) );
|
|
ok_eq( 0, conversion, UINT, "%#x" );
|
|
ok_eq( 0xfeedcafe, sentence, UINT, "%#x" );
|
|
ok_eq( 0, ctx->fdwConversion, UINT, "%#x" );
|
|
ok_eq( 0xfeedcafe, ctx->fdwSentence, UINT, "%#x" );
|
|
|
|
ctx->hWnd = hwnd;
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 1, ImmSetConversionStatus( default_himc, ~0, ~0 ) );
|
|
ok_seq( set_conversion_status_2_seq );
|
|
|
|
ok_ret( 1, ImmGetConversionStatus( default_himc, NULL, &sentence ) );
|
|
ok_eq( ~0, sentence, UINT, "%#x" );
|
|
ok_eq( ~0, ctx->fdwConversion, UINT, "%#x" );
|
|
ok_eq( ~0, ctx->fdwSentence, UINT, "%#x" );
|
|
|
|
ok_ret( 1, ImmSetConversionStatus( default_himc, ~0, ~0 ) );
|
|
ok_seq( empty_sequence );
|
|
|
|
ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) );
|
|
ok_eq( ~0, conversion, UINT, "%#x" );
|
|
ok_eq( ~0, sentence, UINT, "%#x" );
|
|
ok_eq( ~0, ctx->fdwConversion, UINT, "%#x" );
|
|
ok_eq( ~0, ctx->fdwSentence, UINT, "%#x" );
|
|
|
|
/* status is cached and some bits are kept from the previous active IME */
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
todo_wine ok_eq( 0x200, ctx->fdwConversion, UINT, "%#x" );
|
|
ok_eq( old_sentence, ctx->fdwSentence, UINT, "%#x" );
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
ok_eq( ~0, ctx->fdwConversion, UINT, "%#x" );
|
|
ok_eq( ~0, ctx->fdwSentence, UINT, "%#x" );
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
todo_wine ok_eq( 0x200, ctx->fdwConversion, UINT, "%#x" );
|
|
ok_eq( old_sentence, ctx->fdwSentence, UINT, "%#x" );
|
|
|
|
ok_ret( 1, ImmFreeLayout( hkl ) );
|
|
|
|
cleanup:
|
|
/* sanitize conversion status to some sane default */
|
|
ok_ret( 1, ImmSetConversionStatus( default_himc, 0, 0 ) );
|
|
ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) );
|
|
ok_eq( 0, conversion, UINT, "%#x" );
|
|
ok_eq( 0, sentence, UINT, "%#x" );
|
|
ok_eq( 0, ctx->fdwConversion, UINT, "%#x" );
|
|
ok_eq( 0, ctx->fdwSentence, UINT, "%#x" );
|
|
|
|
ok_ret( 1, DestroyWindow( hwnd ) );
|
|
process_messages();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
ok_ret( 1, ImmUnlockIMC( default_himc ) );
|
|
}
|
|
|
|
static void test_ImmSetOpenStatus(void)
|
|
{
|
|
const struct ime_call set_open_status_0_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS},
|
|
},
|
|
{0},
|
|
};
|
|
const struct ime_call set_open_status_1_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS},
|
|
},
|
|
{0},
|
|
};
|
|
const struct ime_call set_open_status_2_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS},
|
|
},
|
|
{0},
|
|
};
|
|
HKL hkl;
|
|
struct ime_windows ime_windows = {0};
|
|
DWORD old_status, status;
|
|
INPUTCONTEXT *ctx;
|
|
HIMC himc;
|
|
HWND hwnd;
|
|
|
|
ok_ret( 0, ImmGetOpenStatus( 0 ) );
|
|
old_status = ImmGetOpenStatus( default_himc );
|
|
|
|
ctx = ImmLockIMC( default_himc );
|
|
ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
|
|
ok_eq( old_status, ctx->fOpen, UINT, "%#x" );
|
|
|
|
hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
process_messages();
|
|
|
|
status = ImmGetOpenStatus( default_himc );
|
|
ok_eq( old_status, status, UINT, "%#x" );
|
|
ok_eq( old_status, ctx->fOpen, UINT, "%#x" );
|
|
|
|
ok_ret( 1, ImmSetOpenStatus( default_himc, 0 ) );
|
|
status = ImmGetOpenStatus( default_himc );
|
|
ok_eq( 0, status, UINT, "%#x" );
|
|
ok_eq( 0, ctx->fOpen, UINT, "%#x" );
|
|
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
|
|
|
|
if (!(hkl = wineime_hkl)) goto cleanup;
|
|
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
ok_ret( 1, ImmLoadIME( hkl ) );
|
|
process_messages();
|
|
/* initial values are dependent on both old and new IME */
|
|
ok_ret( 1, ImmSetOpenStatus( default_himc, 0 ) );
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
status = ImmGetOpenStatus( default_himc );
|
|
ok_eq( 0, status, UINT, "%#x" );
|
|
ok_eq( 0, ctx->fOpen, UINT, "%#x" );
|
|
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 1, ImmSetOpenStatus( default_himc, 0xdeadbeef ) );
|
|
ok_seq( set_open_status_0_seq );
|
|
|
|
status = ImmGetOpenStatus( default_himc );
|
|
ok_eq( 0xdeadbeef, status, UINT, "%#x" );
|
|
ok_eq( 0xdeadbeef, ctx->fOpen, UINT, "%#x" );
|
|
|
|
ok_ret( 1, ImmSetOpenStatus( default_himc, 0xdeadbeef ) );
|
|
ok_seq( empty_sequence );
|
|
|
|
himc = ImmCreateContext();
|
|
ok_ne( NULL, himc, HIMC, "%p" );
|
|
ok_ret( 1, EnumThreadWindows( GetCurrentThreadId(), enum_thread_ime_windows, (LPARAM)&ime_windows ) );
|
|
ok_ne( NULL, ime_windows.ime_hwnd, HWND, "%p" );
|
|
ok_ne( NULL, ime_windows.ime_ui_hwnd, HWND, "%p" );
|
|
ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" );
|
|
ok_ret( 1, ImmSetOpenStatus( himc, TRUE ) );
|
|
ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" );
|
|
ok_ret( 1, ImmSetOpenStatus( himc, FALSE ) );
|
|
ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" );
|
|
ok_ret( 1, ImmDestroyContext( himc ) );
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
status = ImmGetOpenStatus( default_himc );
|
|
ok( status == 0xdeadbeef || status == 0 /* MS Korean IME */, "got status %#lx\n", status );
|
|
ok_eq( status, ctx->fOpen, UINT, "%#x" );
|
|
|
|
ctx->hWnd = 0;
|
|
ok_ret( 1, ImmSetOpenStatus( default_himc, 0xfeedcafe ) );
|
|
ok_seq( set_open_status_1_seq );
|
|
|
|
status = ImmGetOpenStatus( default_himc );
|
|
ok_eq( 0xfeedcafe, status, UINT, "%#x" );
|
|
ok_eq( 0xfeedcafe, ctx->fOpen, UINT, "%#x" );
|
|
|
|
ctx->hWnd = hwnd;
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 1, ImmSetOpenStatus( default_himc, ~0 ) );
|
|
ok_seq( set_open_status_2_seq );
|
|
|
|
status = ImmGetOpenStatus( default_himc );
|
|
ok_eq( ~0, status, UINT, "%#x" );
|
|
ok_eq( ~0, ctx->fOpen, UINT, "%#x" );
|
|
|
|
ok_ret( 1, ImmSetOpenStatus( default_himc, ~0 ) );
|
|
ok_seq( empty_sequence );
|
|
|
|
status = ImmGetOpenStatus( default_himc );
|
|
ok_eq( ~0, status, UINT, "%#x" );
|
|
ok_eq( ~0, ctx->fOpen, UINT, "%#x" );
|
|
|
|
/* status is cached between IME activations */
|
|
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
status = ImmGetOpenStatus( default_himc );
|
|
ok_eq( old_status, status, UINT, "%#x" );
|
|
ok_eq( old_status, ctx->fOpen, UINT, "%#x" );
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
status = ImmGetOpenStatus( default_himc );
|
|
todo_wine ok_eq( 1, status, UINT, "%#x" );
|
|
todo_wine ok_eq( 1, ctx->fOpen, UINT, "%#x" );
|
|
ok_ret( 1, ImmSetOpenStatus( default_himc, 0 ) );
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
status = ImmGetOpenStatus( default_himc );
|
|
ok_eq( old_status, status, UINT, "%#x" );
|
|
ok_eq( old_status, ctx->fOpen, UINT, "%#x" );
|
|
|
|
ok_ret( 1, ImmFreeLayout( hkl ) );
|
|
|
|
cleanup:
|
|
/* sanitize open status to some sane default */
|
|
ok_ret( 1, ImmSetOpenStatus( default_himc, 0 ) );
|
|
status = ImmGetOpenStatus( default_himc );
|
|
ok_eq( 0, status, UINT, "%#x" );
|
|
ok_eq( 0, ctx->fOpen, UINT, "%#x" );
|
|
|
|
ok_ret( 1, DestroyWindow( hwnd ) );
|
|
process_messages();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
ok_ret( 1, ImmUnlockIMC( default_himc ) );
|
|
}
|
|
|
|
static void test_ImmProcessKey(void)
|
|
{
|
|
const struct ime_call process_key_0[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_PROCESS_KEY, .process_key = {.vkey = 'A', .lparam = MAKELONG(0, 0x1e)},
|
|
},
|
|
{0},
|
|
};
|
|
const struct ime_call process_key_1[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_PROCESS_KEY, .process_key = {.vkey = 'A', .lparam = MAKELONG(1, 0x1e)},
|
|
},
|
|
{0},
|
|
};
|
|
const struct ime_call process_key_2[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_PROCESS_KEY, .process_key = {.vkey = 'A', .lparam = MAKELONG(2, 0x1e)},
|
|
},
|
|
{0},
|
|
};
|
|
HKL hkl;
|
|
UINT_PTR ret;
|
|
HIMC himc;
|
|
HWND hwnd;
|
|
|
|
hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
process_messages();
|
|
|
|
ok_ret( 0, ImmProcessKey( hwnd, default_hkl, 'A', MAKELONG(1, 0x1e), 0 ) );
|
|
ok_seq( empty_sequence );
|
|
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
|
|
|
|
if (!(hkl = wineime_hkl)) goto cleanup;
|
|
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
ok_ret( 1, ImmLoadIME( hkl ) );
|
|
process_messages();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
ok_ret( 0, ImmProcessKey( 0, hkl, 'A', MAKELONG(1, 0x1e), 0 ) );
|
|
ok_seq( empty_sequence );
|
|
|
|
ok_ret( 0, ImmProcessKey( hwnd, hkl, 'A', MAKELONG(0, 0x1e), 0 ) );
|
|
ok_seq( process_key_0 );
|
|
ret = ImmProcessKey( hwnd, hkl, 'A', MAKELONG(1, 0x1e), 0 );
|
|
todo_wine
|
|
ok_ret( 2, ret );
|
|
ok_seq( process_key_1 );
|
|
ok_ret( 2, ImmProcessKey( hwnd, hkl, 'A', MAKELONG(2, 0x1e), 0 ) );
|
|
ok_seq( process_key_2 );
|
|
|
|
ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
|
|
ok_ret( 0, ImmProcessKey( hwnd, default_hkl, 'A', MAKELONG(1, 0x1e), 0 ) );
|
|
ok_seq( empty_sequence );
|
|
ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
|
|
|
|
himc = ImmCreateContext();
|
|
ok_ne( NULL, himc, HIMC, "%p" );
|
|
ok_ret( 'A', ImmGetVirtualKey( hwnd ) );
|
|
ok_eq( default_himc, ImmAssociateContext( hwnd, himc ), HIMC, "%p" );
|
|
todo_wine ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( hwnd ) );
|
|
ok_eq( himc, ImmAssociateContext( hwnd, default_himc ), HIMC, "%p" );
|
|
ok_ret( 'A', ImmGetVirtualKey( hwnd ) );
|
|
ImmDestroyContext( himc );
|
|
|
|
ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'A', 0 ) );
|
|
ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( hwnd ) );
|
|
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
|
|
ok_ret( 1, ImmFreeLayout( hkl ) );
|
|
process_messages();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
cleanup:
|
|
ok_ret( 1, DestroyWindow( hwnd ) );
|
|
}
|
|
|
|
static void test_ImmActivateLayout(void)
|
|
{
|
|
const struct ime_call activate_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_SELECT, .select = 1,
|
|
},
|
|
{0},
|
|
};
|
|
const struct ime_call deactivate_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0},
|
|
.todo = TRUE,
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = default_himc,
|
|
.func = IME_SELECT, .select = 0,
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call activate_with_window_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_SELECT, .select = 1,
|
|
.flaky_himc = TRUE,
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/,
|
|
.func = IME_SELECT, .select = 1,
|
|
.flaky_himc = TRUE,
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_SELECT, .wparam = 1, .lparam = (LPARAM)expect_ime},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_OPENSTATUSWINDOW},
|
|
.todo = TRUE, .broken = TRUE, /* broken after SetForegroundWindow(GetDesktopWindow()) as in d3d8:device */
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS},
|
|
.todo = TRUE,
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE},
|
|
.todo = TRUE,
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSENTENCEMODE},
|
|
.todo = TRUE,
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call deactivate_with_window_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0},
|
|
.todo = TRUE, .flaky_himc = TRUE,
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0},
|
|
.todo = TRUE, .flaky_himc = TRUE,
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_CLOSESTATUSWINDOW},
|
|
.todo = TRUE, .broken = TRUE, /* broken after SetForegroundWindow(GetDesktopWindow()) as in d3d8:device */
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_SELECT, .wparam = 0, .lparam = (LPARAM)expect_ime},
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = default_himc,
|
|
.func = IME_SELECT, .select = 0,
|
|
.flaky_himc = TRUE,
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/,
|
|
.func = IME_SELECT, .select = 0,
|
|
.flaky_himc = TRUE,
|
|
},
|
|
{0},
|
|
};
|
|
HKL hkl;
|
|
struct ime_windows ime_windows = {0};
|
|
HIMC himc;
|
|
HWND hwnd;
|
|
UINT ret;
|
|
|
|
SET_ENABLE( ImeInquire, TRUE );
|
|
SET_ENABLE( ImeDestroy, TRUE );
|
|
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
|
|
|
|
if (!(hkl = wineime_hkl)) goto cleanup;
|
|
|
|
/* ActivateKeyboardLayout doesn't call ImeInquire / ImeDestroy */
|
|
|
|
ok_seq( empty_sequence );
|
|
ok_eq( default_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" );
|
|
ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
|
|
ok_eq( hkl, ActivateKeyboardLayout( default_hkl, 0 ), HKL, "%p" );
|
|
ok_seq( empty_sequence );
|
|
ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
|
|
|
|
|
|
/* ImmActivateLayout changes active HKL */
|
|
|
|
SET_EXPECT( ImeInquire );
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
ok_seq( activate_seq );
|
|
CHECK_CALLED( ImeInquire );
|
|
ok_ret( 1, ImmLoadIME( hkl ) );
|
|
|
|
ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
|
|
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
ok_seq( empty_sequence );
|
|
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
ok_seq( deactivate_seq );
|
|
|
|
ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
|
|
|
|
|
|
/* ImmActivateLayout leaks the IME, we need to free it manually */
|
|
|
|
SET_EXPECT( ImeDestroy );
|
|
ret = ImmFreeLayout( hkl );
|
|
ok( ret, "ImmFreeLayout returned %u\n", ret );
|
|
CHECK_CALLED( ImeDestroy );
|
|
ok_seq( empty_sequence );
|
|
|
|
|
|
/* when there's a window, ActivateKeyboardLayout calls ImeInquire */
|
|
|
|
hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
process_messages();
|
|
ok_seq( empty_sequence );
|
|
|
|
himc = ImmCreateContext();
|
|
ok( !!himc, "got himc %p\n", himc );
|
|
ok_seq( empty_sequence );
|
|
|
|
SET_EXPECT( ImeInquire );
|
|
ok_eq( default_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" );
|
|
CHECK_CALLED( ImeInquire );
|
|
activate_with_window_seq[1].himc = himc;
|
|
ok_seq( activate_with_window_seq );
|
|
ok_ret( 1, ImmLoadIME( hkl ) );
|
|
|
|
ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
|
|
|
|
/* FIXME: ignore spurious VK_CONTROL ImeProcessKey / ImeToAsciiEx calls */
|
|
process_messages();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
ok_eq( hkl, ActivateKeyboardLayout( default_hkl, 0 ), HKL, "%p" );
|
|
deactivate_with_window_seq[1].himc = himc;
|
|
deactivate_with_window_seq[5].himc = himc;
|
|
ok_seq( deactivate_with_window_seq );
|
|
|
|
ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
|
|
|
|
ok_ret( 1, ImmDestroyContext( himc ) );
|
|
ok_seq( empty_sequence );
|
|
|
|
|
|
ok_eq( default_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" );
|
|
ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
|
|
|
|
ok_ret( 1, EnumThreadWindows( GetCurrentThreadId(), enum_thread_ime_windows, (LPARAM)&ime_windows ) );
|
|
ok( !!ime_windows.ime_hwnd, "missing IME window\n" );
|
|
ok( !!ime_windows.ime_ui_hwnd, "missing IME UI window\n" );
|
|
ok_ret( (UINT_PTR)ime_windows.ime_hwnd, (UINT_PTR)GetParent( ime_windows.ime_ui_hwnd ) );
|
|
|
|
ok_eq( hkl, ActivateKeyboardLayout( default_hkl, 0 ), HKL, "%p" );
|
|
ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
|
|
process_messages();
|
|
|
|
|
|
SET_EXPECT( ImeDestroy );
|
|
ok_ret( 1, ImmFreeLayout( hkl ) );
|
|
CHECK_CALLED( ImeDestroy );
|
|
|
|
ok_ret( 1, DestroyWindow( hwnd ) );
|
|
process_messages();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
|
|
cleanup:
|
|
SET_ENABLE( ImeInquire, FALSE );
|
|
SET_ENABLE( ImeDestroy, FALSE );
|
|
}
|
|
|
|
static void test_ImmCreateInputContext(void)
|
|
{
|
|
struct ime_call activate_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_SELECT, .select = 1,
|
|
.flaky_himc = TRUE,
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc[0]*/,
|
|
.func = IME_SELECT, .select = 1,
|
|
.flaky_himc = TRUE,
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_SELECT, .wparam = 1, .lparam = (LPARAM)expect_ime},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_OPENSTATUSWINDOW},
|
|
.todo = TRUE, .broken = TRUE, /* broken after SetForegroundWindow(GetDesktopWindow()) as in d3d8:device */
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call select1_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc[0]*/,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS},
|
|
.todo = TRUE, .flaky_himc = TRUE, .broken = TRUE /* sometimes */,
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS},
|
|
.todo = TRUE, .flaky_himc = TRUE, .broken = TRUE /* sometimes */,
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS},
|
|
.todo = TRUE, .broken = TRUE /* sometimes */,
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE},
|
|
.todo = TRUE, .broken = TRUE /* sometimes */,
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc[1]*/,
|
|
.func = IME_SELECT, .select = 1,
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call select0_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc[1]*/,
|
|
.func = IME_SELECT, .select = 0,
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call deactivate_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0},
|
|
.todo = TRUE, .flaky_himc = TRUE,
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc[0]*/,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0},
|
|
.todo = TRUE, .flaky_himc = TRUE,
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_CLOSESTATUSWINDOW},
|
|
.todo = TRUE, .broken = TRUE, /* broken after SetForegroundWindow(GetDesktopWindow()) as in d3d8:device */
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_SELECT, .wparam = 0, .lparam = (LPARAM)expect_ime},
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = default_himc,
|
|
.func = IME_SELECT, .select = 0,
|
|
.flaky_himc = TRUE,
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc[0]*/,
|
|
.func = IME_SELECT, .select = 0,
|
|
.flaky_himc = TRUE,
|
|
},
|
|
{0},
|
|
};
|
|
HKL hkl;
|
|
INPUTCONTEXT *ctx;
|
|
HIMC himc[2];
|
|
HWND hwnd;
|
|
|
|
ctx = ImmLockIMC( default_himc );
|
|
ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
|
|
ok_ret( 0, IsWindow( ctx->hWnd ) );
|
|
ok_ret( 1, ImmUnlockIMC( default_himc ) );
|
|
|
|
|
|
/* new input contexts cannot be locked before IME window has been created */
|
|
|
|
himc[0] = ImmCreateContext();
|
|
ok( !!himc[0], "ImmCreateContext failed, error %lu\n", GetLastError() );
|
|
ctx = ImmLockIMC( himc[0] );
|
|
todo_wine
|
|
ok( !ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
|
|
if (ctx) ImmUnlockIMCC( himc[0] );
|
|
|
|
hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
process_messages();
|
|
|
|
ctx = ImmLockIMC( default_himc );
|
|
ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
|
|
ok_ret( 1, ImmUnlockIMC( default_himc ) );
|
|
|
|
ctx = ImmLockIMC( himc[0] );
|
|
ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
|
|
ok_ret( 1, ImmUnlockIMC( himc[0] ) );
|
|
|
|
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
|
|
ime_info.dwPrivateDataSize = 123;
|
|
|
|
if (!(hkl = wineime_hkl)) goto cleanup;
|
|
|
|
ok_ret( 1, ImmLoadIME( hkl ) );
|
|
|
|
/* Activating the layout calls ImeSelect 1 on existing HIMC */
|
|
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
activate_seq[1].himc = himc[0];
|
|
ok_seq( activate_seq );
|
|
|
|
ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
|
|
|
|
ctx = ImmLockIMC( default_himc );
|
|
ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
|
|
ok_ret( 1, ImmUnlockIMC( default_himc ) );
|
|
|
|
ctx = ImmLockIMC( himc[0] );
|
|
ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
|
|
ok_ret( 1, ImmUnlockIMC( himc[0] ) );
|
|
|
|
|
|
/* ImmLockIMC triggers the ImeSelect call, to allocate private data */
|
|
|
|
himc[1] = ImmCreateContext();
|
|
ok( !!himc[1], "ImmCreateContext failed, error %lu\n", GetLastError() );
|
|
|
|
ok_seq( empty_sequence );
|
|
ctx = ImmLockIMC( himc[1] );
|
|
ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
|
|
select1_seq[0].himc = himc[0];
|
|
select1_seq[4].himc = himc[1];
|
|
ok_seq( select1_seq );
|
|
|
|
ok_ret( 1, ImmUnlockIMC( himc[1] ) );
|
|
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 1, ImmDestroyContext( himc[1] ) );
|
|
select0_seq[0].himc = himc[1];
|
|
ok_seq( select0_seq );
|
|
|
|
|
|
/* Deactivating the layout calls ImeSelect 0 on existing HIMC */
|
|
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
deactivate_seq[1].himc = himc[0];
|
|
deactivate_seq[5].himc = himc[0];
|
|
ok_seq( deactivate_seq );
|
|
|
|
ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" );
|
|
|
|
ok_ret( 1, ImmFreeLayout( hkl ) );
|
|
ok_seq( empty_sequence );
|
|
|
|
cleanup:
|
|
ok_ret( 1, ImmDestroyContext( himc[0] ) );
|
|
ok_ret( 1, DestroyWindow( hwnd ) );
|
|
process_messages();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
ime_info.dwPrivateDataSize = 0;
|
|
}
|
|
|
|
static void test_DefWindowProc(void)
|
|
{
|
|
const struct ime_call start_composition_seq[] =
|
|
{
|
|
{.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_STARTCOMPOSITION}},
|
|
{0},
|
|
};
|
|
const struct ime_call end_composition_seq[] =
|
|
{
|
|
{.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_ENDCOMPOSITION}},
|
|
{0},
|
|
};
|
|
const struct ime_call composition_seq[] =
|
|
{
|
|
{.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_COMPOSITION}},
|
|
{0},
|
|
};
|
|
const struct ime_call set_context_seq[] =
|
|
{
|
|
{.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_SETCONTEXT}},
|
|
{0},
|
|
};
|
|
const struct ime_call notify_seq[] =
|
|
{
|
|
{.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY}},
|
|
{0},
|
|
};
|
|
HKL hkl;
|
|
UINT_PTR ret;
|
|
HWND hwnd;
|
|
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
|
|
|
|
if (!(hkl = wineime_hkl)) return;
|
|
|
|
hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
ok_ret( 1, ImmLoadIME( hkl ) );
|
|
process_messages();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
ok_ret( 0, DefWindowProcW( hwnd, WM_IME_STARTCOMPOSITION, 0, 0 ) );
|
|
ok_seq( start_composition_seq );
|
|
ok_ret( 0, DefWindowProcW( hwnd, WM_IME_ENDCOMPOSITION, 0, 0 ) );
|
|
ok_seq( end_composition_seq );
|
|
ok_ret( 0, DefWindowProcW( hwnd, WM_IME_COMPOSITION, 0, 0 ) );
|
|
ok_seq( composition_seq );
|
|
ok_ret( 0, DefWindowProcW( hwnd, WM_IME_SETCONTEXT, 0, 0 ) );
|
|
ok_seq( set_context_seq );
|
|
ok_ret( 0, DefWindowProcW( hwnd, WM_IME_NOTIFY, 0, 0 ) );
|
|
ok_seq( notify_seq );
|
|
ok_ret( 0, DefWindowProcW( hwnd, WM_IME_CONTROL, 0, 0 ) );
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 0, DefWindowProcW( hwnd, WM_IME_COMPOSITIONFULL, 0, 0 ) );
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 0, DefWindowProcW( hwnd, WM_IME_SELECT, 0, 0 ) );
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 0, DefWindowProcW( hwnd, WM_IME_CHAR, 0, 0 ) );
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 0, DefWindowProcW( hwnd, 0x287, 0, 0 ) );
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 0, DefWindowProcW( hwnd, WM_IME_REQUEST, 0, 0 ) );
|
|
ok_seq( empty_sequence );
|
|
ret = DefWindowProcW( hwnd, WM_IME_KEYDOWN, 0, 0 );
|
|
todo_wine
|
|
ok_ret( 0, ret );
|
|
ok_seq( empty_sequence );
|
|
ret = DefWindowProcW( hwnd, WM_IME_KEYUP, 0, 0 );
|
|
todo_wine
|
|
ok_ret( 0, ret );
|
|
ok_seq( empty_sequence );
|
|
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
ok_ret( 1, DestroyWindow( hwnd ) );
|
|
process_messages();
|
|
|
|
ok_ret( 1, ImmFreeLayout( hkl ) );
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
}
|
|
|
|
static void test_ImmSetActiveContext(void)
|
|
{
|
|
const struct ime_call activate_0_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_SET_ACTIVE_CONTEXT, .set_active_context = {.flag = 1}
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_SETCONTEXT, .wparam = 1, .lparam = ISC_SHOWUIALL}
|
|
},
|
|
{0},
|
|
};
|
|
const struct ime_call deactivate_0_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_SET_ACTIVE_CONTEXT, .set_active_context = {.flag = 0}
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_SETCONTEXT, .wparam = 0, .lparam = ISC_SHOWUIALL}
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call deactivate_1_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS},
|
|
.todo = TRUE, .flaky_himc = TRUE, .broken = TRUE /* sometimes */,
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS},
|
|
.todo = TRUE, .broken = TRUE /* sometimes */,
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE},
|
|
.todo = TRUE, .broken = TRUE /* sometimes */,
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/,
|
|
.func = IME_SELECT, .select = 1,
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/,
|
|
.func = IME_SET_ACTIVE_CONTEXT, .set_active_context = {.flag = 0}
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_SETCONTEXT, .wparam = 0, .lparam = ISC_SHOWUIALL}
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call deactivate_2_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/,
|
|
.func = IME_SET_ACTIVE_CONTEXT, .set_active_context = {.flag = 0}
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_SETCONTEXT, .wparam = 0, .lparam = ISC_SHOWUIALL}
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call activate_1_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/,
|
|
.func = IME_SET_ACTIVE_CONTEXT, .set_active_context = {.flag = 1}
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_SETCONTEXT, .wparam = 1, .lparam = ISC_SHOWUIALL}
|
|
},
|
|
{0},
|
|
};
|
|
HKL hkl;
|
|
struct ime_windows ime_windows = {0};
|
|
INPUTCONTEXT *ctx;
|
|
HIMC himc;
|
|
HWND hwnd;
|
|
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
|
|
|
|
if (!(hkl = wineime_hkl)) return;
|
|
|
|
hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
ok_ret( 1, ImmLoadIME( hkl ) );
|
|
process_messages();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
ok_ret( 1, EnumThreadWindows( GetCurrentThreadId(), enum_thread_ime_windows, (LPARAM)&ime_windows ) );
|
|
ok_ne( NULL, ime_windows.ime_hwnd, HWND, "%p" );
|
|
ok_ne( NULL, ime_windows.ime_ui_hwnd, HWND, "%p" );
|
|
ok_ret( 0, IsWindowVisible( ime_windows.ime_ui_hwnd ) );
|
|
|
|
SetLastError( 0xdeadbeef );
|
|
ok_ret( 1, ImmSetActiveContext( hwnd, default_himc, TRUE ) );
|
|
ok_seq( activate_0_seq );
|
|
ok_ret( 0, GetLastError() );
|
|
ok_ret( 0, IsWindowVisible( ime_windows.ime_ui_hwnd ) );
|
|
ok_ret( 1, ImmSetActiveContext( hwnd, default_himc, TRUE ) );
|
|
ok_seq( activate_0_seq );
|
|
ok_ret( 1, ImmSetActiveContext( hwnd, default_himc, FALSE ) );
|
|
ok_seq( deactivate_0_seq );
|
|
|
|
himc = ImmCreateContext();
|
|
ok_ne( NULL, himc, HIMC, "%p" );
|
|
ctx = ImmLockIMC( himc );
|
|
ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
|
|
ok_eq( 0, ctx->hWnd, HWND, "%p" );
|
|
|
|
ok_ret( 1, ImmSetActiveContext( hwnd, himc, FALSE ) );
|
|
deactivate_1_seq[3].himc = himc;
|
|
deactivate_1_seq[4].himc = himc;
|
|
ok_seq( deactivate_1_seq );
|
|
ok_ret( 1, ImmSetActiveContext( hwnd, himc, TRUE ) );
|
|
activate_1_seq[0].himc = himc;
|
|
ok_seq( activate_1_seq );
|
|
|
|
ctx->hWnd = (HWND)0xdeadbeef;
|
|
ok_ret( 1, ImmSetActiveContext( hwnd, himc, FALSE ) );
|
|
ok_eq( (HWND)0xdeadbeef, ctx->hWnd, HWND, "%p" );
|
|
deactivate_2_seq[0].himc = himc;
|
|
ok_seq( deactivate_2_seq );
|
|
|
|
ctx->hWnd = 0;
|
|
ok_ret( 1, ImmSetActiveContext( hwnd, himc, TRUE ) );
|
|
ok_eq( hwnd, ctx->hWnd, HWND, "%p" );
|
|
activate_1_seq[0].himc = himc;
|
|
ok_seq( activate_1_seq );
|
|
|
|
ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" );
|
|
ok_ret( 1, ImmSetActiveContext( hwnd, himc, TRUE ) );
|
|
ok_ret( 0, IsWindowVisible( ime_windows.ime_ui_hwnd ) );
|
|
ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" );
|
|
|
|
ctx->hWnd = 0;
|
|
ok_eq( default_himc, ImmAssociateContext( hwnd, himc ), HIMC, "%p" );
|
|
ok_eq( himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" );
|
|
ok_ret( 0, IsWindowVisible( ime_windows.ime_ui_hwnd ) );
|
|
ok_eq( hwnd, ctx->hWnd, HWND, "%p" );
|
|
|
|
ctx->hWnd = (HWND)0xdeadbeef;
|
|
ok_eq( himc, ImmGetContext( hwnd ), HIMC, "%p" );
|
|
ok_eq( (HWND)0xdeadbeef, ctx->hWnd, HWND, "%p" );
|
|
ok_ret( 1, ImmReleaseContext( hwnd, himc ) );
|
|
|
|
ok_ret( 1, ImmUnlockIMC( himc ) );
|
|
ok_ret( 1, ImmDestroyContext( himc ) );
|
|
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
ok_ret( 1, DestroyWindow( hwnd ) );
|
|
process_messages();
|
|
|
|
ok_ret( 1, ImmFreeLayout( hkl ) );
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
}
|
|
|
|
static void test_ImmRequestMessage(void)
|
|
{
|
|
struct ime_call composition_window_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_COMPOSITIONWINDOW, .lparam = 0/*&comp_form*/}
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call candidate_window_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_CANDIDATEWINDOW, .lparam = 0/*&cand_form*/}
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call composition_font_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_COMPOSITIONFONT, .lparam = 0/*&log_font*/}
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call reconvert_string_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_RECONVERTSTRING, .lparam = 0/*&reconv*/}
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call confirm_reconvert_string_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_CONFIRMRECONVERTSTRING, .lparam = 0/*&reconv*/}
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call query_char_position_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_QUERYCHARPOSITION, .lparam = 0/*&char_pos*/}
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call document_feed_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_DOCUMENTFEED, .lparam = 0/*&reconv*/}
|
|
},
|
|
{0},
|
|
};
|
|
HKL hkl;
|
|
COMPOSITIONFORM comp_form = {0};
|
|
IMECHARPOSITION char_pos = {0};
|
|
RECONVERTSTRING reconv = {0};
|
|
CANDIDATEFORM cand_form = {0};
|
|
LOGFONTW log_font = {0};
|
|
INPUTCONTEXT *ctx;
|
|
HIMC himc;
|
|
HWND hwnd;
|
|
|
|
if (!(hkl = wineime_hkl)) return;
|
|
|
|
hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
|
|
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
ok_ret( 1, ImmLoadIME( hkl ) );
|
|
himc = ImmCreateContext();
|
|
ok_ne( NULL, himc, HIMC, "%p" );
|
|
ctx = ImmLockIMC( himc );
|
|
ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
|
|
process_messages();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
ok_ret( 0, ImmRequestMessageW( default_himc, 0xdeadbeef, 0 ) );
|
|
todo_wine ok_seq( empty_sequence );
|
|
ok_ret( 0, ImmRequestMessageW( default_himc, IMR_COMPOSITIONWINDOW, (LPARAM)&comp_form ) );
|
|
composition_window_seq[0].message.lparam = (LPARAM)&comp_form;
|
|
ok_seq( composition_window_seq );
|
|
ok_ret( 0, ImmRequestMessageW( default_himc, IMR_CANDIDATEWINDOW, (LPARAM)&cand_form ) );
|
|
candidate_window_seq[0].message.lparam = (LPARAM)&cand_form;
|
|
ok_seq( candidate_window_seq );
|
|
ok_ret( 0, ImmRequestMessageW( default_himc, IMR_COMPOSITIONFONT, (LPARAM)&log_font ) );
|
|
composition_font_seq[0].message.lparam = (LPARAM)&log_font;
|
|
ok_seq( composition_font_seq );
|
|
ok_ret( 0, ImmRequestMessageW( default_himc, IMR_RECONVERTSTRING, (LPARAM)&reconv ) );
|
|
todo_wine ok_seq( empty_sequence );
|
|
reconv.dwSize = sizeof(RECONVERTSTRING);
|
|
ok_ret( 0, ImmRequestMessageW( default_himc, IMR_RECONVERTSTRING, (LPARAM)&reconv ) );
|
|
reconvert_string_seq[0].message.lparam = (LPARAM)&reconv;
|
|
ok_seq( reconvert_string_seq );
|
|
ok_ret( 0, ImmRequestMessageW( default_himc, IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) );
|
|
confirm_reconvert_string_seq[0].message.lparam = (LPARAM)&reconv;
|
|
ok_seq( confirm_reconvert_string_seq );
|
|
ok_ret( 0, ImmRequestMessageW( default_himc, IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) );
|
|
query_char_position_seq[0].message.lparam = (LPARAM)&char_pos;
|
|
ok_seq( query_char_position_seq );
|
|
ok_ret( 0, ImmRequestMessageW( default_himc, IMR_DOCUMENTFEED, (LPARAM)&reconv ) );
|
|
document_feed_seq[0].message.lparam = (LPARAM)&reconv;
|
|
ok_seq( document_feed_seq );
|
|
|
|
ok_ret( 0, ImmRequestMessageW( himc, IMR_CANDIDATEWINDOW, (LPARAM)&cand_form ) );
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 1, ImmSetActiveContext( hwnd, himc, TRUE ) );
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
ok_ret( 0, ImmRequestMessageW( himc, IMR_CANDIDATEWINDOW, (LPARAM)&cand_form ) );
|
|
candidate_window_seq[0].message.lparam = (LPARAM)&cand_form;
|
|
ok_seq( candidate_window_seq );
|
|
ok_ret( 0, ImmRequestMessageW( default_himc, IMR_CANDIDATEWINDOW, (LPARAM)&cand_form ) );
|
|
ok_seq( candidate_window_seq );
|
|
|
|
ok_ret( 1, ImmUnlockIMC( himc ) );
|
|
ok_ret( 1, ImmDestroyContext( himc ) );
|
|
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
ok_ret( 1, DestroyWindow( hwnd ) );
|
|
process_messages();
|
|
|
|
ok_ret( 1, ImmFreeLayout( hkl ) );
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
}
|
|
|
|
static void test_ImmGetCandidateList( BOOL unicode )
|
|
{
|
|
char buffer[512], expect_bufW[512] = {0}, expect_bufA[512] = {0};
|
|
CANDIDATELIST *cand_list = (CANDIDATELIST *)buffer, *expect_listW, *expect_listA;
|
|
HKL hkl;
|
|
CANDIDATEINFO *cand_info;
|
|
INPUTCONTEXT *ctx;
|
|
HIMC himc;
|
|
HWND hwnd;
|
|
|
|
expect_listW = (CANDIDATELIST *)expect_bufW;
|
|
expect_listW->dwSize = offsetof(CANDIDATELIST, dwOffset[2]) + 32 * sizeof(WCHAR);
|
|
expect_listW->dwStyle = 0xdeadbeef;
|
|
expect_listW->dwCount = 2;
|
|
expect_listW->dwSelection = 3;
|
|
expect_listW->dwPageStart = 4;
|
|
expect_listW->dwPageSize = 5;
|
|
expect_listW->dwOffset[0] = offsetof(CANDIDATELIST, dwOffset[2]) + 2 * sizeof(WCHAR);
|
|
expect_listW->dwOffset[1] = offsetof(CANDIDATELIST, dwOffset[2]) + 16 * sizeof(WCHAR);
|
|
wcscpy( (WCHAR *)(expect_bufW + expect_listW->dwOffset[0]), L"Candidate 1" );
|
|
wcscpy( (WCHAR *)(expect_bufW + expect_listW->dwOffset[1]), L"Candidate 2" );
|
|
|
|
expect_listA = (CANDIDATELIST *)expect_bufA;
|
|
expect_listA->dwSize = offsetof(CANDIDATELIST, dwOffset[2]) + 32;
|
|
expect_listA->dwStyle = 0xdeadbeef;
|
|
expect_listA->dwCount = 2;
|
|
expect_listA->dwSelection = 3;
|
|
expect_listA->dwPageStart = 4;
|
|
expect_listA->dwPageSize = 5;
|
|
expect_listA->dwOffset[0] = offsetof(CANDIDATELIST, dwOffset[2]) + 2;
|
|
expect_listA->dwOffset[1] = offsetof(CANDIDATELIST, dwOffset[2]) + 16;
|
|
strcpy( (char *)(expect_bufA + expect_listA->dwOffset[0]), "Candidate 1" );
|
|
strcpy( (char *)(expect_bufA + expect_listA->dwOffset[1]), "Candidate 2" );
|
|
|
|
winetest_push_context( unicode ? "unicode" : "ansi" );
|
|
|
|
/* IME_PROP_END_UNLOAD for the IME to unload / reload. */
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD;
|
|
if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
|
|
|
|
if (!(hkl = wineime_hkl)) goto cleanup;
|
|
|
|
hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
ok_ret( 1, ImmLoadIME( hkl ) );
|
|
himc = ImmCreateContext();
|
|
ok_ne( NULL, himc, HIMC, "%p" );
|
|
ctx = ImmLockIMC( himc );
|
|
ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
|
|
process_messages();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
ok_ret( 0, ImmGetCandidateListW( default_himc, 0, NULL, 0 ) );
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 0, ImmGetCandidateListW( default_himc, 1, NULL, 0 ) );
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 0, ImmGetCandidateListW( default_himc, 0, cand_list, sizeof(buffer) ) );
|
|
ok_seq( empty_sequence );
|
|
|
|
ok_ret( 0, ImmGetCandidateListW( himc, 0, cand_list, sizeof(buffer) ) );
|
|
ok_seq( empty_sequence );
|
|
|
|
todo_wine ok_seq( empty_sequence );
|
|
ctx->hCandInfo = ImmReSizeIMCC( ctx->hCandInfo, sizeof(*cand_info) + sizeof(buffer) );
|
|
ok( !!ctx->hCandInfo, "ImmReSizeIMCC failed, error %lu\n", GetLastError() );
|
|
|
|
cand_info = ImmLockIMCC( ctx->hCandInfo );
|
|
ok( !!cand_info, "ImmLockIMCC failed, error %lu\n", GetLastError() );
|
|
cand_info->dwCount = 1;
|
|
cand_info->dwOffset[0] = sizeof(*cand_info);
|
|
if (unicode) memcpy( cand_info + 1, expect_bufW, sizeof(expect_bufW) );
|
|
else memcpy( cand_info + 1, expect_bufA, sizeof(expect_bufA) );
|
|
ok_ret( 0, ImmUnlockIMCC( ctx->hCandInfo ) );
|
|
|
|
ok_ret( (unicode ? 96 : 80), ImmGetCandidateListW( himc, 0, NULL, 0 ) );
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 0, ImmGetCandidateListW( himc, 1, NULL, 0 ) );
|
|
ok_seq( empty_sequence );
|
|
memset( buffer, 0xcd, sizeof(buffer) );
|
|
ok_ret( (unicode ? 96 : 80), ImmGetCandidateListW( himc, 0, cand_list, sizeof(buffer) ) );
|
|
ok_seq( empty_sequence );
|
|
|
|
if (!unicode)
|
|
{
|
|
expect_listW->dwSize = offsetof(CANDIDATELIST, dwOffset[2]) + 24 * sizeof(WCHAR);
|
|
expect_listW->dwOffset[0] = offsetof(CANDIDATELIST, dwOffset[2]);
|
|
expect_listW->dwOffset[1] = offsetof(CANDIDATELIST, dwOffset[2]) + 12 * sizeof(WCHAR);
|
|
wcscpy( (WCHAR *)(expect_bufW + expect_listW->dwOffset[0]), L"Candidate 1" );
|
|
wcscpy( (WCHAR *)(expect_bufW + expect_listW->dwOffset[1]), L"Candidate 2" );
|
|
}
|
|
check_candidate_list_( __LINE__, cand_list, expect_listW, TRUE );
|
|
|
|
ok_ret( (unicode ? 56 : 64), ImmGetCandidateListA( himc, 0, NULL, 0 ) );
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 0, ImmGetCandidateListA( himc, 1, NULL, 0 ) );
|
|
ok_seq( empty_sequence );
|
|
memset( buffer, 0xcd, sizeof(buffer) );
|
|
ok_ret( (unicode ? 56 : 64), ImmGetCandidateListA( himc, 0, cand_list, sizeof(buffer) ) );
|
|
ok_seq( empty_sequence );
|
|
|
|
if (unicode)
|
|
{
|
|
expect_listA->dwSize = offsetof(CANDIDATELIST, dwOffset[2]) + 24;
|
|
expect_listA->dwOffset[0] = offsetof(CANDIDATELIST, dwOffset[2]);
|
|
expect_listA->dwOffset[1] = offsetof(CANDIDATELIST, dwOffset[2]) + 12;
|
|
strcpy( (char *)(expect_bufA + expect_listA->dwOffset[0]), "Candidate 1" );
|
|
strcpy( (char *)(expect_bufA + expect_listA->dwOffset[1]), "Candidate 2" );
|
|
}
|
|
check_candidate_list_( __LINE__, cand_list, expect_listA, FALSE );
|
|
|
|
ok_ret( 1, ImmUnlockIMC( himc ) );
|
|
ok_ret( 1, ImmDestroyContext( himc ) );
|
|
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
ok_ret( 1, DestroyWindow( hwnd ) );
|
|
process_messages();
|
|
|
|
ok_ret( 1, ImmFreeLayout( hkl ) );
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
cleanup:
|
|
winetest_pop_context();
|
|
}
|
|
|
|
static void test_ImmGetCandidateListCount( BOOL unicode )
|
|
{
|
|
HKL hkl;
|
|
CANDIDATEINFO *cand_info;
|
|
INPUTCONTEXT *ctx;
|
|
DWORD count;
|
|
HIMC himc;
|
|
HWND hwnd;
|
|
|
|
winetest_push_context( unicode ? "unicode" : "ansi" );
|
|
|
|
/* IME_PROP_END_UNLOAD for the IME to unload / reload. */
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD;
|
|
if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
|
|
|
|
if (!(hkl = wineime_hkl)) goto cleanup;
|
|
|
|
hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
ok_ret( 1, ImmLoadIME( hkl ) );
|
|
himc = ImmCreateContext();
|
|
ok_ne( NULL, himc, HIMC, "%p" );
|
|
ctx = ImmLockIMC( himc );
|
|
ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
|
|
process_messages();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
ok_ret( 144, ImmGetCandidateListCountW( default_himc, &count ) );
|
|
ok_eq( 0, count, UINT, "%u" );
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 144, ImmGetCandidateListCountA( default_himc, &count ) );
|
|
ok_eq( 0, count, UINT, "%u" );
|
|
ok_seq( empty_sequence );
|
|
|
|
ok_ret( 144, ImmGetCandidateListCountW( himc, &count ) );
|
|
ok_eq( 0, count, UINT, "%u" );
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 144, ImmGetCandidateListCountA( himc, &count ) );
|
|
ok_eq( 0, count, UINT, "%u" );
|
|
ok_seq( empty_sequence );
|
|
|
|
cand_info = ImmLockIMCC( ctx->hCandInfo );
|
|
ok( !!cand_info, "ImmLockIMCC failed, error %lu\n", GetLastError() );
|
|
cand_info->dwCount = 1;
|
|
ok_ret( 0, ImmUnlockIMCC( ctx->hCandInfo ) );
|
|
|
|
todo_wine_if(!unicode)
|
|
ok_ret( (unicode ? 144 : 172), ImmGetCandidateListCountW( himc, &count ) );
|
|
ok_eq( 1, count, UINT, "%u" );
|
|
ok_seq( empty_sequence );
|
|
todo_wine_if(unicode)
|
|
ok_ret( (unicode ? 172 : 144), ImmGetCandidateListCountA( himc, &count ) );
|
|
ok_eq( 1, count, UINT, "%u" );
|
|
ok_seq( empty_sequence );
|
|
|
|
ok_ret( 1, ImmUnlockIMC( himc ) );
|
|
ok_ret( 1, ImmDestroyContext( himc ) );
|
|
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
ok_ret( 1, DestroyWindow( hwnd ) );
|
|
process_messages();
|
|
|
|
ok_ret( 1, ImmFreeLayout( hkl ) );
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
cleanup:
|
|
winetest_pop_context();
|
|
}
|
|
|
|
static void test_ImmGetCandidateWindow(void)
|
|
{
|
|
HKL hkl;
|
|
const CANDIDATEFORM expect_form =
|
|
{
|
|
.dwIndex = 0xdeadbeef,
|
|
.dwStyle = 0xfeedcafe,
|
|
.ptCurrentPos = {.x = 123, .y = 456},
|
|
.rcArea = {.left = 1, .top = 2, .right = 3, .bottom = 4},
|
|
};
|
|
CANDIDATEFORM cand_form;
|
|
INPUTCONTEXT *ctx;
|
|
HIMC himc;
|
|
HWND hwnd;
|
|
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
|
|
|
|
if (!(hkl = wineime_hkl)) return;
|
|
|
|
hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
ok_ret( 1, ImmLoadIME( hkl ) );
|
|
himc = ImmCreateContext();
|
|
ok_ne( NULL, himc, HIMC, "%p" );
|
|
ctx = ImmLockIMC( himc );
|
|
ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
|
|
process_messages();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
memset( &cand_form, 0xcd, sizeof(cand_form) );
|
|
ok_ret( 0, ImmGetCandidateWindow( default_himc, 0, &cand_form ) );
|
|
ok_eq( 0xcdcdcdcd, cand_form.dwIndex, UINT, "%u" );
|
|
ok_ret( 0, ImmGetCandidateWindow( default_himc, 1, &cand_form ) );
|
|
ok_eq( 0xcdcdcdcd, cand_form.dwIndex, UINT, "%u" );
|
|
ok_ret( 0, ImmGetCandidateWindow( default_himc, 2, &cand_form ) );
|
|
ok_eq( 0xcdcdcdcd, cand_form.dwIndex, UINT, "%u" );
|
|
ok_ret( 0, ImmGetCandidateWindow( default_himc, 3, &cand_form ) );
|
|
ok_eq( 0xcdcdcdcd, cand_form.dwIndex, UINT, "%u" );
|
|
ok_ret( 1, ImmGetCandidateWindow( default_himc, 4, &cand_form ) );
|
|
ok_seq( empty_sequence );
|
|
|
|
ok_ret( 0, ImmGetCandidateWindow( himc, 0, &cand_form ) );
|
|
ok_seq( empty_sequence );
|
|
|
|
ok_seq( empty_sequence );
|
|
ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
|
|
ctx->cfCandForm[0] = expect_form;
|
|
|
|
ok_ret( 1, ImmGetCandidateWindow( himc, 0, &cand_form ) );
|
|
check_candidate_form( &cand_form, &expect_form );
|
|
ok_seq( empty_sequence );
|
|
|
|
ok_seq( empty_sequence );
|
|
ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() );
|
|
ctx->cfCandForm[0].dwIndex = -1;
|
|
|
|
ok_ret( 0, ImmGetCandidateWindow( himc, 0, &cand_form ) );
|
|
ok_seq( empty_sequence );
|
|
|
|
ok_ret( 1, ImmUnlockIMC( himc ) );
|
|
ok_ret( 1, ImmDestroyContext( himc ) );
|
|
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
ok_ret( 1, DestroyWindow( hwnd ) );
|
|
process_messages();
|
|
|
|
ok_ret( 1, ImmFreeLayout( hkl ) );
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
}
|
|
|
|
static void test_ImmGetCompositionString( BOOL unicode )
|
|
{
|
|
static COMPOSITIONSTRING expect_string_empty = {.dwSize = sizeof(COMPOSITIONSTRING)};
|
|
static COMPOSITIONSTRING expect_stringA =
|
|
{
|
|
.dwSize = 176,
|
|
.dwCompReadAttrLen = 8,
|
|
.dwCompReadAttrOffset = 116,
|
|
.dwCompReadClauseLen = 8,
|
|
.dwCompReadClauseOffset = 108,
|
|
.dwCompReadStrLen = 8,
|
|
.dwCompReadStrOffset = 100,
|
|
.dwCompAttrLen = 4,
|
|
.dwCompAttrOffset = 136,
|
|
.dwCompClauseLen = 8,
|
|
.dwCompClauseOffset = 128,
|
|
.dwCompStrLen = 4,
|
|
.dwCompStrOffset = 124,
|
|
.dwCursorPos = 3,
|
|
.dwDeltaStart = 1,
|
|
.dwResultReadClauseLen = 8,
|
|
.dwResultReadClauseOffset = 150,
|
|
.dwResultReadStrLen = 10,
|
|
.dwResultReadStrOffset = 140,
|
|
.dwResultClauseLen = 8,
|
|
.dwResultClauseOffset = 164,
|
|
.dwResultStrLen = 6,
|
|
.dwResultStrOffset = 158,
|
|
.dwPrivateSize = 4,
|
|
.dwPrivateOffset = 172,
|
|
};
|
|
static const COMPOSITIONSTRING expect_stringW =
|
|
{
|
|
.dwSize = 204,
|
|
.dwCompReadAttrLen = 8,
|
|
.dwCompReadAttrOffset = 124,
|
|
.dwCompReadClauseLen = 8,
|
|
.dwCompReadClauseOffset = 116,
|
|
.dwCompReadStrLen = 8,
|
|
.dwCompReadStrOffset = 100,
|
|
.dwCompAttrLen = 4,
|
|
.dwCompAttrOffset = 148,
|
|
.dwCompClauseLen = 8,
|
|
.dwCompClauseOffset = 140,
|
|
.dwCompStrLen = 4,
|
|
.dwCompStrOffset = 132,
|
|
.dwCursorPos = 3,
|
|
.dwDeltaStart = 1,
|
|
.dwResultReadClauseLen = 8,
|
|
.dwResultReadClauseOffset = 172,
|
|
.dwResultReadStrLen = 10,
|
|
.dwResultReadStrOffset = 152,
|
|
.dwResultClauseLen = 8,
|
|
.dwResultClauseOffset = 192,
|
|
.dwResultStrLen = 6,
|
|
.dwResultStrOffset = 180,
|
|
.dwPrivateSize = 4,
|
|
.dwPrivateOffset = 200,
|
|
};
|
|
static const UINT gcs_indexes[] =
|
|
{
|
|
GCS_COMPREADSTR,
|
|
GCS_COMPREADATTR,
|
|
GCS_COMPREADCLAUSE,
|
|
GCS_COMPSTR,
|
|
GCS_COMPATTR,
|
|
GCS_COMPCLAUSE,
|
|
GCS_CURSORPOS,
|
|
GCS_DELTASTART,
|
|
GCS_RESULTREADSTR,
|
|
GCS_RESULTREADCLAUSE,
|
|
GCS_RESULTSTR,
|
|
GCS_RESULTCLAUSE,
|
|
};
|
|
static const UINT scs_indexes[] =
|
|
{
|
|
SCS_SETSTR,
|
|
SCS_CHANGEATTR,
|
|
SCS_CHANGECLAUSE,
|
|
};
|
|
static const UINT expect_retW[ARRAY_SIZE(gcs_indexes)] = {16, 8, 8, 8, 4, 8, 3, 1, 20, 8, 12, 8};
|
|
static const UINT expect_retA[ARRAY_SIZE(gcs_indexes)] = {8, 8, 8, 4, 4, 8, 3, 1, 10, 8, 6, 8};
|
|
HKL hkl;
|
|
COMPOSITIONSTRING *string;
|
|
char buffer[1024];
|
|
INPUTCONTEXT *old_ctx, *ctx;
|
|
const void *str;
|
|
HIMCC old_himcc;
|
|
UINT i, len;
|
|
BYTE *dst;
|
|
HIMC himc;
|
|
HWND hwnd;
|
|
|
|
SET_ENABLE( ImeSetCompositionString, TRUE );
|
|
|
|
winetest_push_context( unicode ? "unicode" : "ansi" );
|
|
|
|
/* IME_PROP_END_UNLOAD for the IME to unload / reload. */
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD;
|
|
if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
|
|
|
|
if (!(hkl = wineime_hkl)) goto cleanup;
|
|
|
|
hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
ok_ret( 1, ImmLoadIME( hkl ) );
|
|
himc = ImmCreateContext();
|
|
ok_ne( NULL, himc, HIMC, "%p" );
|
|
ctx = ImmLockIMC( himc );
|
|
ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
|
|
process_messages();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
memset( buffer, 0xcd, sizeof(buffer) );
|
|
todo_wine ok_ret( -2, ImmGetCompositionStringW( default_himc, GCS_COMPSTR | GCS_COMPATTR, buffer, sizeof(buffer) ) );
|
|
memset( buffer, 0xcd, sizeof(buffer) );
|
|
todo_wine ok_ret( -2, ImmGetCompositionStringA( default_himc, GCS_COMPSTR | GCS_COMPATTR, buffer, sizeof(buffer) ) );
|
|
|
|
for (i = 0; i < ARRAY_SIZE(gcs_indexes); ++i)
|
|
{
|
|
memset( buffer, 0xcd, sizeof(buffer) );
|
|
ok_ret( 0, ImmGetCompositionStringW( default_himc, gcs_indexes[i], buffer, sizeof(buffer) ) );
|
|
memset( buffer, 0xcd, sizeof(buffer) );
|
|
ok_ret( 0, ImmGetCompositionStringA( default_himc, gcs_indexes[i], buffer, sizeof(buffer) ) );
|
|
|
|
memset( buffer, 0xcd, sizeof(buffer) );
|
|
ok_ret( 0, ImmGetCompositionStringW( himc, gcs_indexes[i], buffer, sizeof(buffer) ) );
|
|
memset( buffer, 0xcd, sizeof(buffer) );
|
|
ok_ret( 0, ImmGetCompositionStringA( himc, gcs_indexes[i], buffer, sizeof(buffer) ) );
|
|
}
|
|
|
|
ctx->hCompStr = ImmReSizeIMCC( ctx->hCompStr, unicode ? expect_stringW.dwSize : expect_stringA.dwSize );
|
|
string = ImmLockIMCC( ctx->hCompStr );
|
|
ok( !!string, "ImmLockIMCC failed, error %lu\n", GetLastError() );
|
|
check_composition_string( string, &expect_string_empty );
|
|
|
|
string->dwCursorPos = 3;
|
|
string->dwDeltaStart = 1;
|
|
|
|
if (unicode) str = L"ReadComp";
|
|
else str = "ReadComp";
|
|
len = unicode ? wcslen( str ) : strlen( str );
|
|
|
|
string->dwCompReadStrLen = len;
|
|
string->dwCompReadStrOffset = string->dwSize;
|
|
dst = (BYTE *)string + string->dwCompReadStrOffset;
|
|
memcpy( dst, str, len * (unicode ? sizeof(WCHAR) : 1) );
|
|
string->dwSize += len * (unicode ? sizeof(WCHAR) : 1);
|
|
|
|
string->dwCompReadClauseLen = 2 * sizeof(DWORD);
|
|
string->dwCompReadClauseOffset = string->dwSize;
|
|
dst = (BYTE *)string + string->dwCompReadClauseOffset;
|
|
*(DWORD *)(dst + 0 * sizeof(DWORD)) = 0;
|
|
*(DWORD *)(dst + 1 * sizeof(DWORD)) = len;
|
|
string->dwSize += 2 * sizeof(DWORD);
|
|
|
|
string->dwCompReadAttrLen = len;
|
|
string->dwCompReadAttrOffset = string->dwSize;
|
|
dst = (BYTE *)string + string->dwCompReadAttrOffset;
|
|
memset( dst, ATTR_INPUT, len );
|
|
string->dwSize += len;
|
|
|
|
if (unicode) str = L"Comp";
|
|
else str = "Comp";
|
|
len = unicode ? wcslen( str ) : strlen( str );
|
|
|
|
string->dwCompStrLen = len;
|
|
string->dwCompStrOffset = string->dwSize;
|
|
dst = (BYTE *)string + string->dwCompStrOffset;
|
|
memcpy( dst, str, len * (unicode ? sizeof(WCHAR) : 1) );
|
|
string->dwSize += len * (unicode ? sizeof(WCHAR) : 1);
|
|
|
|
string->dwCompClauseLen = 2 * sizeof(DWORD);
|
|
string->dwCompClauseOffset = string->dwSize;
|
|
dst = (BYTE *)string + string->dwCompClauseOffset;
|
|
*(DWORD *)(dst + 0 * sizeof(DWORD)) = 0;
|
|
*(DWORD *)(dst + 1 * sizeof(DWORD)) = len;
|
|
string->dwSize += 2 * sizeof(DWORD);
|
|
|
|
string->dwCompAttrLen = len;
|
|
string->dwCompAttrOffset = string->dwSize;
|
|
dst = (BYTE *)string + string->dwCompAttrOffset;
|
|
memset( dst, ATTR_INPUT, len );
|
|
string->dwSize += len;
|
|
|
|
if (unicode) str = L"ReadResult";
|
|
else str = "ReadResult";
|
|
len = unicode ? wcslen( str ) : strlen( str );
|
|
|
|
string->dwResultReadStrLen = len;
|
|
string->dwResultReadStrOffset = string->dwSize;
|
|
dst = (BYTE *)string + string->dwResultReadStrOffset;
|
|
memcpy( dst, str, len * (unicode ? sizeof(WCHAR) : 1) );
|
|
string->dwSize += len * (unicode ? sizeof(WCHAR) : 1);
|
|
|
|
string->dwResultReadClauseLen = 2 * sizeof(DWORD);
|
|
string->dwResultReadClauseOffset = string->dwSize;
|
|
dst = (BYTE *)string + string->dwResultReadClauseOffset;
|
|
*(DWORD *)(dst + 0 * sizeof(DWORD)) = 0;
|
|
*(DWORD *)(dst + 1 * sizeof(DWORD)) = len;
|
|
string->dwSize += 2 * sizeof(DWORD);
|
|
|
|
if (unicode) str = L"Result";
|
|
else str = "Result";
|
|
len = unicode ? wcslen( str ) : strlen( str );
|
|
|
|
string->dwResultStrLen = len;
|
|
string->dwResultStrOffset = string->dwSize;
|
|
dst = (BYTE *)string + string->dwResultStrOffset;
|
|
memcpy( dst, str, len * (unicode ? sizeof(WCHAR) : 1) );
|
|
string->dwSize += len * (unicode ? sizeof(WCHAR) : 1);
|
|
|
|
string->dwResultClauseLen = 2 * sizeof(DWORD);
|
|
string->dwResultClauseOffset = string->dwSize;
|
|
dst = (BYTE *)string + string->dwResultClauseOffset;
|
|
*(DWORD *)(dst + 0 * sizeof(DWORD)) = 0;
|
|
*(DWORD *)(dst + 1 * sizeof(DWORD)) = len;
|
|
string->dwSize += 2 * sizeof(DWORD);
|
|
|
|
string->dwPrivateSize = 4;
|
|
string->dwPrivateOffset = string->dwSize;
|
|
dst = (BYTE *)string + string->dwPrivateOffset;
|
|
memset( dst, 0xa5, string->dwPrivateSize );
|
|
string->dwSize += 4;
|
|
|
|
check_composition_string( string, unicode ? &expect_stringW : &expect_stringA );
|
|
ok_ret( 0, ImmUnlockIMCC( ctx->hCompStr ) );
|
|
old_himcc = ctx->hCompStr;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(gcs_indexes); ++i)
|
|
{
|
|
UINT_PTR expect;
|
|
|
|
winetest_push_context( "%u", i );
|
|
|
|
memset( buffer, 0xcd, sizeof(buffer) );
|
|
expect = expect_retW[i];
|
|
ok_ret( expect, ImmGetCompositionStringW( himc, gcs_indexes[i], buffer, sizeof(buffer) ) );
|
|
memset( buffer + expect, 0, 4 );
|
|
|
|
if (i == 0) ok_wcs( L"ReadComp", (WCHAR *)buffer );
|
|
else if (i == 3) ok_wcs( L"Comp", (WCHAR *)buffer );
|
|
else if (i == 8) ok_wcs( L"ReadResult", (WCHAR *)buffer );
|
|
else if (i == 10) ok_wcs( L"Result", (WCHAR *)buffer );
|
|
else if (i != 6 && i != 7) ok_wcs( L"", (WCHAR *)buffer );
|
|
|
|
memset( buffer, 0xcd, sizeof(buffer) );
|
|
expect = expect_retA[i];
|
|
ok_ret( expect, ImmGetCompositionStringA( himc, gcs_indexes[i], buffer, sizeof(buffer) ) );
|
|
memset( buffer + expect, 0, 4 );
|
|
|
|
if (i == 0) ok_str( "ReadComp", (char *)buffer );
|
|
else if (i == 3) ok_str( "Comp", (char *)buffer );
|
|
else if (i == 8) ok_str( "ReadResult", (char *)buffer );
|
|
else if (i == 10) ok_str( "Result", (char *)buffer );
|
|
else if (i != 6 && i != 7) ok_str( "", (char *)buffer );
|
|
|
|
winetest_pop_context();
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(gcs_indexes); ++i)
|
|
{
|
|
winetest_push_context( "%u", i );
|
|
ok_ret( 0, ImmSetCompositionStringW( himc, gcs_indexes[i], buffer, sizeof(buffer), NULL, 0 ) );
|
|
ok_ret( 0, ImmSetCompositionStringA( himc, gcs_indexes[i], buffer, sizeof(buffer), NULL, 0 ) );
|
|
winetest_pop_context();
|
|
}
|
|
ok_ret( 0, ImmSetCompositionStringW( himc, SCS_SETSTR | SCS_CHANGEATTR, buffer, sizeof(buffer), NULL, 0 ) );
|
|
ok_ret( 0, ImmSetCompositionStringA( himc, SCS_SETSTR | SCS_CHANGEATTR, buffer, sizeof(buffer), NULL, 0 ) );
|
|
ok_ret( 0, ImmSetCompositionStringW( himc, SCS_CHANGECLAUSE | SCS_CHANGEATTR, buffer, sizeof(buffer), NULL, 0 ) );
|
|
ok_ret( 0, ImmSetCompositionStringA( himc, SCS_CHANGECLAUSE | SCS_CHANGEATTR, buffer, sizeof(buffer), NULL, 0 ) );
|
|
|
|
for (i = 0; i < ARRAY_SIZE(scs_indexes); ++i)
|
|
{
|
|
winetest_push_context( "%u", i );
|
|
|
|
if (scs_indexes[i] == SCS_CHANGECLAUSE)
|
|
{
|
|
memset( buffer, 0, sizeof(buffer) );
|
|
*((DWORD *)buffer + 1) = 1;
|
|
len = 2 * sizeof(DWORD);
|
|
}
|
|
else if (scs_indexes[i] == SCS_CHANGEATTR)
|
|
{
|
|
memset( buffer, 0xcd, sizeof(buffer) );
|
|
len = expect_stringW.dwCompAttrLen;
|
|
}
|
|
else if (scs_indexes[i] == SCS_SETSTR)
|
|
{
|
|
wcscpy( (WCHAR *)buffer, L"CompString" );
|
|
len = 11 * sizeof(WCHAR);
|
|
}
|
|
|
|
todo_ImeSetCompositionString = !unicode;
|
|
SET_EXPECT( ImeSetCompositionString );
|
|
ok_ret( 1, ImmSetCompositionStringW( himc, scs_indexes[i], buffer, len, NULL, 0 ) );
|
|
CHECK_CALLED( ImeSetCompositionString );
|
|
todo_ImeSetCompositionString = FALSE;
|
|
ok_seq( empty_sequence );
|
|
|
|
string = ImmLockIMCC( ctx->hCompStr );
|
|
ok_ne( NULL, string, COMPOSITIONSTRING *, "%p" );
|
|
check_composition_string( string, unicode ? &expect_stringW : &expect_stringA );
|
|
ok_ret( 0, ImmUnlockIMCC( ctx->hCompStr ) );
|
|
|
|
if (scs_indexes[i] == SCS_CHANGECLAUSE)
|
|
{
|
|
memset( buffer, 0, sizeof(buffer) );
|
|
*((DWORD *)buffer + 1) = 1;
|
|
len = 2 * sizeof(DWORD);
|
|
}
|
|
else if (scs_indexes[i] == SCS_CHANGEATTR)
|
|
{
|
|
memset( buffer, 0xcd, sizeof(buffer) );
|
|
len = expect_stringA.dwCompAttrLen;
|
|
}
|
|
else if (scs_indexes[i] == SCS_SETSTR)
|
|
{
|
|
strcpy( buffer, "CompString" );
|
|
len = 11;
|
|
}
|
|
|
|
todo_ImeSetCompositionString = unicode;
|
|
SET_EXPECT( ImeSetCompositionString );
|
|
ok_ret( 1, ImmSetCompositionStringA( himc, scs_indexes[i], buffer, len, NULL, 0 ) );
|
|
CHECK_CALLED( ImeSetCompositionString );
|
|
todo_ImeSetCompositionString = FALSE;
|
|
ok_seq( empty_sequence );
|
|
|
|
string = ImmLockIMCC( ctx->hCompStr );
|
|
ok_ne( NULL, string, COMPOSITIONSTRING *, "%p" );
|
|
check_composition_string( string, unicode ? &expect_stringW : &expect_stringA );
|
|
ok_ret( 0, ImmUnlockIMCC( ctx->hCompStr ) );
|
|
|
|
winetest_pop_context();
|
|
}
|
|
ok_seq( empty_sequence );
|
|
|
|
old_ctx = ctx;
|
|
ok_ret( 1, ImmUnlockIMC( himc ) );
|
|
|
|
/* composition strings are kept between IME selections */
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
ctx = ImmLockIMC( himc );
|
|
ok_eq( old_ctx, ctx, INPUTCONTEXT *, "%p" );
|
|
ok_eq( old_himcc, ctx->hCompStr, HIMCC, "%p" );
|
|
string = ImmLockIMCC( ctx->hCompStr );
|
|
ok_ne( NULL, string, COMPOSITIONSTRING *, "%p" );
|
|
*string = expect_string_empty;
|
|
ok_ret( 0, ImmUnlockIMCC( ctx->hCompStr ) );
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
ok_eq( old_himcc, ctx->hCompStr, HIMCC, "%p" );
|
|
check_composition_string( string, &expect_string_empty );
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
ok_eq( old_himcc, ctx->hCompStr, HIMCC, "%p" );
|
|
check_composition_string( string, &expect_string_empty );
|
|
|
|
ok_ret( 1, ImmUnlockIMC( himc ) );
|
|
ok_ret( 1, ImmDestroyContext( himc ) );
|
|
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
ok_ret( 1, DestroyWindow( hwnd ) );
|
|
process_messages();
|
|
|
|
ok_ret( 1, ImmFreeLayout( hkl ) );
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
cleanup:
|
|
winetest_pop_context();
|
|
SET_ENABLE( ImeSetCompositionString, FALSE );
|
|
}
|
|
|
|
static void test_ImmSetCompositionWindow(void)
|
|
{
|
|
struct ime_call set_composition_window_0_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCOMPOSITIONWINDOW},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCOMPOSITIONWINDOW},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCOMPOSITIONWINDOW},
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call set_composition_window_1_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCOMPOSITIONWINDOW},
|
|
},
|
|
{0},
|
|
};
|
|
COMPOSITIONFORM comp_form, expect_form =
|
|
{
|
|
.dwStyle = 0xfeedcafe,
|
|
.ptCurrentPos = {.x = 123, .y = 456},
|
|
.rcArea = {.left = 1, .top = 2, .right = 3, .bottom = 4},
|
|
};
|
|
struct ime_windows ime_windows = {0};
|
|
INPUTCONTEXT *ctx;
|
|
HIMC himc;
|
|
HWND hwnd;
|
|
HKL hkl;
|
|
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
|
|
|
|
if (!(hkl = wineime_hkl)) return;
|
|
|
|
hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
ok_ret( 1, ImmLoadIME( hkl ) );
|
|
himc = ImmCreateContext();
|
|
ok_ne( NULL, himc, HIMC, "%p" );
|
|
ctx = ImmLockIMC( himc );
|
|
ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
|
|
process_messages();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
set_composition_window_0_seq[0].himc = himc;
|
|
set_composition_window_1_seq[0].himc = himc;
|
|
|
|
ok_ret( 1, EnumThreadWindows( GetCurrentThreadId(), enum_thread_ime_windows, (LPARAM)&ime_windows ) );
|
|
ok_ne( NULL, ime_windows.ime_hwnd, HWND, "%p" );
|
|
ok_ne( NULL, ime_windows.ime_ui_hwnd, HWND, "%p" );
|
|
|
|
ctx->cfCompForm = expect_form;
|
|
ctx->fdwInit = ~INIT_COMPFORM;
|
|
memset( &comp_form, 0xcd, sizeof(comp_form) );
|
|
ok_ret( 0, ImmGetCompositionWindow( himc, &comp_form ) );
|
|
ok_eq( 0xcdcdcdcd, comp_form.dwStyle, UINT, "%#x" );
|
|
ctx->fdwInit = INIT_COMPFORM;
|
|
ok_ret( 1, ImmGetCompositionWindow( himc, &comp_form ) );
|
|
check_composition_form( &comp_form, &expect_form );
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 0, IsWindowVisible( ime_windows.ime_ui_hwnd ) );
|
|
|
|
ok_ret( 0, ShowWindow( ime_windows.ime_ui_hwnd, SW_SHOWNOACTIVATE ) );
|
|
process_messages();
|
|
ok_seq( empty_sequence );
|
|
check_WM_SHOWWINDOW = TRUE;
|
|
|
|
ctx->hWnd = hwnd;
|
|
ctx->fdwInit = 0;
|
|
memset( &comp_form, 0xcd, sizeof(comp_form) );
|
|
ok_ret( 1, ImmSetCompositionWindow( himc, &comp_form ) );
|
|
process_messages();
|
|
ok_seq( set_composition_window_0_seq );
|
|
ok_eq( INIT_COMPFORM, ctx->fdwInit, UINT, "%u" );
|
|
check_composition_form( &ctx->cfCompForm, &comp_form );
|
|
ok_ret( 1, IsWindowVisible( ime_windows.ime_ui_hwnd ) );
|
|
check_WM_SHOWWINDOW = FALSE;
|
|
|
|
ShowWindow( ime_windows.ime_ui_hwnd, SW_HIDE );
|
|
process_messages();
|
|
ok_seq( empty_sequence );
|
|
|
|
ok_ret( 1, ImmSetCompositionWindow( himc, &expect_form ) );
|
|
ok_seq( set_composition_window_0_seq );
|
|
check_composition_form( &ctx->cfCompForm, &expect_form );
|
|
|
|
ctx->cfCompForm = expect_form;
|
|
ok_ret( 1, ImmGetCompositionWindow( himc, &comp_form ) );
|
|
check_composition_form( &comp_form, &expect_form );
|
|
ok_seq( empty_sequence );
|
|
|
|
ctx->hWnd = 0;
|
|
memset( &comp_form, 0xcd, sizeof(comp_form) );
|
|
ok_ret( 1, ImmSetCompositionWindow( himc, &comp_form ) );
|
|
ok_seq( set_composition_window_1_seq );
|
|
check_composition_form( &ctx->cfCompForm, &comp_form );
|
|
|
|
ok_ret( 1, ImmUnlockIMC( himc ) );
|
|
ok_ret( 1, ImmDestroyContext( himc ) );
|
|
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
ok_ret( 1, DestroyWindow( hwnd ) );
|
|
process_messages();
|
|
|
|
ok_ret( 1, ImmFreeLayout( hkl ) );
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
}
|
|
|
|
static void test_ImmSetStatusWindowPos(void)
|
|
{
|
|
struct ime_call set_status_window_pos_0_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETSTATUSWINDOWPOS},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSTATUSWINDOWPOS},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSTATUSWINDOWPOS},
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call set_status_window_pos_1_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETSTATUSWINDOWPOS},
|
|
},
|
|
{0},
|
|
};
|
|
INPUTCONTEXT *ctx;
|
|
POINT pos;
|
|
HIMC himc;
|
|
HWND hwnd;
|
|
HKL hkl;
|
|
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
|
|
|
|
if (!(hkl = wineime_hkl)) return;
|
|
|
|
hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
ok_ret( 1, ImmLoadIME( hkl ) );
|
|
himc = ImmCreateContext();
|
|
ok_ne( NULL, himc, HIMC, "%p" );
|
|
ctx = ImmLockIMC( himc );
|
|
ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
|
|
process_messages();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
set_status_window_pos_0_seq[0].himc = himc;
|
|
set_status_window_pos_1_seq[0].himc = himc;
|
|
|
|
memset( &pos, 0xcd, sizeof(pos) );
|
|
ctx->ptStatusWndPos.x = 0xdeadbeef;
|
|
ctx->ptStatusWndPos.y = 0xfeedcafe;
|
|
ctx->fdwInit = ~INIT_STATUSWNDPOS;
|
|
ok_ret( 0, ImmGetStatusWindowPos( himc, &pos ) );
|
|
ok_eq( 0xcdcdcdcd, pos.x, UINT, "%u" );
|
|
ok_eq( 0xcdcdcdcd, pos.y, UINT, "%u" );
|
|
ctx->fdwInit = INIT_STATUSWNDPOS;
|
|
ok_ret( 1, ImmGetStatusWindowPos( himc, &pos ) );
|
|
ok_eq( 0xdeadbeef, pos.x, UINT, "%u" );
|
|
ok_eq( 0xfeedcafe, pos.y, UINT, "%u" );
|
|
ok_seq( empty_sequence );
|
|
|
|
pos.x = 123;
|
|
pos.y = 456;
|
|
ctx->hWnd = hwnd;
|
|
ctx->fdwInit = 0;
|
|
ok_ret( 1, ImmSetStatusWindowPos( himc, &pos ) );
|
|
ok_seq( set_status_window_pos_0_seq );
|
|
ok_eq( INIT_STATUSWNDPOS, ctx->fdwInit, UINT, "%u" );
|
|
|
|
ok_ret( 1, ImmSetStatusWindowPos( himc, &pos ) );
|
|
ok_seq( set_status_window_pos_0_seq );
|
|
ok_ret( 1, ImmGetStatusWindowPos( himc, &pos ) );
|
|
ok_eq( 123, pos.x, UINT, "%u" );
|
|
ok_eq( 123, ctx->ptStatusWndPos.x, UINT, "%u" );
|
|
ok_eq( 456, pos.y, UINT, "%u" );
|
|
ok_eq( 456, ctx->ptStatusWndPos.y, UINT, "%u" );
|
|
ok_seq( empty_sequence );
|
|
|
|
ctx->hWnd = 0;
|
|
ok_ret( 1, ImmSetStatusWindowPos( himc, &pos ) );
|
|
ok_seq( set_status_window_pos_1_seq );
|
|
|
|
ok_ret( 1, ImmUnlockIMC( himc ) );
|
|
ok_ret( 1, ImmDestroyContext( himc ) );
|
|
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
ok_ret( 1, DestroyWindow( hwnd ) );
|
|
process_messages();
|
|
|
|
ok_ret( 1, ImmFreeLayout( hkl ) );
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
}
|
|
|
|
static void test_ImmSetCompositionFont( BOOL unicode )
|
|
{
|
|
struct ime_call set_composition_font_0_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCOMPOSITIONFONT},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCOMPOSITIONFONT},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCOMPOSITIONFONT},
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call set_composition_font_1_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCOMPOSITIONFONT},
|
|
},
|
|
{0},
|
|
};
|
|
LOGFONTW fontW, expect_fontW =
|
|
{
|
|
.lfHeight = 1,
|
|
.lfWidth = 2,
|
|
.lfEscapement = 3,
|
|
.lfOrientation = 4,
|
|
.lfWeight = 5,
|
|
.lfItalic = 6,
|
|
.lfUnderline = 7,
|
|
.lfStrikeOut = 8,
|
|
.lfCharSet = 8,
|
|
.lfOutPrecision = 10,
|
|
.lfClipPrecision = 11,
|
|
.lfQuality = 12,
|
|
.lfPitchAndFamily = 13,
|
|
.lfFaceName = L"FontFace",
|
|
};
|
|
LOGFONTA fontA, expect_fontA =
|
|
{
|
|
.lfHeight = 1,
|
|
.lfWidth = 2,
|
|
.lfEscapement = 3,
|
|
.lfOrientation = 4,
|
|
.lfWeight = 5,
|
|
.lfItalic = 6,
|
|
.lfUnderline = 7,
|
|
.lfStrikeOut = 8,
|
|
.lfCharSet = 8,
|
|
.lfOutPrecision = 10,
|
|
.lfClipPrecision = 11,
|
|
.lfQuality = 12,
|
|
.lfPitchAndFamily = 13,
|
|
.lfFaceName = "FontFace",
|
|
};
|
|
INPUTCONTEXT *ctx;
|
|
HIMC himc;
|
|
HWND hwnd;
|
|
HKL hkl;
|
|
|
|
winetest_push_context( unicode ? "unicode" : "ansi" );
|
|
|
|
/* IME_PROP_END_UNLOAD for the IME to unload / reload. */
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD;
|
|
if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE;
|
|
|
|
if (!(hkl = wineime_hkl)) goto cleanup;
|
|
|
|
hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
ok_ret( 1, ImmLoadIME( hkl ) );
|
|
himc = ImmCreateContext();
|
|
ok_ne( NULL, himc, HIMC, "%p" );
|
|
ctx = ImmLockIMC( himc );
|
|
ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
|
|
process_messages();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
set_composition_font_0_seq[0].himc = himc;
|
|
set_composition_font_1_seq[0].himc = himc;
|
|
|
|
memset( &fontW, 0xcd, sizeof(fontW) );
|
|
memset( &fontA, 0xcd, sizeof(fontA) );
|
|
|
|
if (unicode) ctx->lfFont.W = expect_fontW;
|
|
else ctx->lfFont.A = expect_fontA;
|
|
ctx->fdwInit = ~INIT_LOGFONT;
|
|
ok_ret( 0, ImmGetCompositionFontW( himc, &fontW ) );
|
|
ok_ret( 0, ImmGetCompositionFontA( himc, &fontA ) );
|
|
ctx->fdwInit = INIT_LOGFONT;
|
|
ok_ret( 1, ImmGetCompositionFontW( himc, &fontW ) );
|
|
check_logfont_w( &fontW, &expect_fontW );
|
|
ok_ret( 1, ImmGetCompositionFontA( himc, &fontA ) );
|
|
check_logfont_a( &fontA, &expect_fontA );
|
|
|
|
ctx->hWnd = hwnd;
|
|
ctx->fdwInit = 0;
|
|
memset( &ctx->lfFont, 0xcd, sizeof(ctx->lfFont) );
|
|
ok_ret( 1, ImmSetCompositionFontW( himc, &expect_fontW ) );
|
|
ok_eq( INIT_LOGFONT, ctx->fdwInit, UINT, "%u" );
|
|
ok_seq( set_composition_font_0_seq );
|
|
ok_ret( 1, ImmSetCompositionFontW( himc, &expect_fontW ) );
|
|
ok_seq( set_composition_font_0_seq );
|
|
if (unicode) check_logfont_w( &ctx->lfFont.W, &expect_fontW );
|
|
else check_logfont_a( &ctx->lfFont.A, &expect_fontA );
|
|
|
|
ok_ret( 1, ImmGetCompositionFontW( himc, &fontW ) );
|
|
check_logfont_w( &fontW, &expect_fontW );
|
|
ok_ret( 1, ImmGetCompositionFontA( himc, &fontA ) );
|
|
check_logfont_a( &fontA, &expect_fontA );
|
|
|
|
ctx->hWnd = hwnd;
|
|
ctx->fdwInit = 0;
|
|
memset( &ctx->lfFont, 0xcd, sizeof(ctx->lfFont) );
|
|
ok_ret( 1, ImmSetCompositionFontA( himc, &expect_fontA ) );
|
|
ok_eq( INIT_LOGFONT, ctx->fdwInit, UINT, "%u" );
|
|
ok_seq( set_composition_font_0_seq );
|
|
ok_ret( 1, ImmSetCompositionFontA( himc, &expect_fontA ) );
|
|
ok_seq( set_composition_font_0_seq );
|
|
if (unicode) check_logfont_w( &ctx->lfFont.W, &expect_fontW );
|
|
else check_logfont_a( &ctx->lfFont.A, &expect_fontA );
|
|
|
|
ok_ret( 1, ImmGetCompositionFontW( himc, &fontW ) );
|
|
check_logfont_w( &fontW, &expect_fontW );
|
|
ok_ret( 1, ImmGetCompositionFontA( himc, &fontA ) );
|
|
check_logfont_a( &fontA, &expect_fontA );
|
|
|
|
ctx->hWnd = 0;
|
|
ok_ret( 1, ImmSetCompositionFontW( himc, &expect_fontW ) );
|
|
ok_seq( set_composition_font_1_seq );
|
|
ok_ret( 1, ImmSetCompositionFontA( himc, &expect_fontA ) );
|
|
ok_seq( set_composition_font_1_seq );
|
|
|
|
ok_ret( 1, ImmUnlockIMC( himc ) );
|
|
ok_ret( 1, ImmDestroyContext( himc ) );
|
|
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
ok_ret( 1, DestroyWindow( hwnd ) );
|
|
process_messages();
|
|
|
|
ok_ret( 1, ImmFreeLayout( hkl ) );
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
cleanup:
|
|
winetest_pop_context();
|
|
}
|
|
|
|
static void test_ImmSetCandidateWindow(void)
|
|
{
|
|
struct ime_call set_candidate_window_0_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCANDIDATEPOS},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCANDIDATEPOS, .lparam = 4},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCANDIDATEPOS, .lparam = 4},
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call set_candidate_window_1_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/,
|
|
.func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCANDIDATEPOS},
|
|
},
|
|
{0},
|
|
};
|
|
CANDIDATEFORM cand_form, expect_form =
|
|
{
|
|
.dwIndex = 2, .dwStyle = 0xfeedcafe,
|
|
.ptCurrentPos = {.x = 123, .y = 456},
|
|
.rcArea = {.left = 1, .top = 2, .right = 3, .bottom = 4},
|
|
};
|
|
INPUTCONTEXT *ctx;
|
|
HIMC himc;
|
|
HWND hwnd;
|
|
HKL hkl;
|
|
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
|
|
|
|
if (!(hkl = wineime_hkl)) return;
|
|
|
|
hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
ok_ret( 1, ImmLoadIME( hkl ) );
|
|
himc = ImmCreateContext();
|
|
ok_ne( NULL, himc, HIMC, "%p" );
|
|
ctx = ImmLockIMC( himc );
|
|
ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
|
|
process_messages();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
set_candidate_window_0_seq[0].himc = himc;
|
|
set_candidate_window_1_seq[0].himc = himc;
|
|
|
|
ctx->cfCandForm[1] = expect_form;
|
|
ctx->cfCandForm[2] = expect_form;
|
|
ctx->fdwInit = 0;
|
|
memset( &cand_form, 0xcd, sizeof(cand_form) );
|
|
ok_ret( 0, ImmGetCandidateWindow( himc, 0, &cand_form ) );
|
|
ok_eq( 0xcdcdcdcd, cand_form.dwStyle, UINT, "%#x" );
|
|
ok_ret( 1, ImmGetCandidateWindow( himc, 1, &cand_form ) );
|
|
check_candidate_form( &cand_form, &expect_form );
|
|
ok_ret( 1, ImmGetCandidateWindow( himc, 2, &cand_form ) );
|
|
check_candidate_form( &cand_form, &expect_form );
|
|
ok_seq( empty_sequence );
|
|
|
|
ctx->hWnd = hwnd;
|
|
memset( &cand_form, 0xcd, sizeof(cand_form) );
|
|
cand_form.dwIndex = 2;
|
|
ok_ret( 1, ImmSetCandidateWindow( himc, &cand_form ) );
|
|
ok_seq( set_candidate_window_0_seq );
|
|
check_candidate_form( &ctx->cfCandForm[2], &cand_form );
|
|
ok_eq( 0, ctx->fdwInit, UINT, "%u" );
|
|
|
|
ok_ret( 1, ImmSetCandidateWindow( himc, &expect_form ) );
|
|
ok_seq( set_candidate_window_0_seq );
|
|
check_candidate_form( &ctx->cfCandForm[2], &expect_form );
|
|
|
|
ctx->hWnd = 0;
|
|
memset( &cand_form, 0xcd, sizeof(cand_form) );
|
|
cand_form.dwIndex = 2;
|
|
ok_ret( 1, ImmSetCandidateWindow( himc, &cand_form ) );
|
|
ok_seq( set_candidate_window_1_seq );
|
|
check_candidate_form( &ctx->cfCandForm[2], &cand_form );
|
|
|
|
ok_ret( 1, ImmUnlockIMC( himc ) );
|
|
ok_ret( 1, ImmDestroyContext( himc ) );
|
|
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
ok_ret( 1, DestroyWindow( hwnd ) );
|
|
process_messages();
|
|
|
|
ok_ret( 1, ImmFreeLayout( hkl ) );
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
}
|
|
|
|
static void test_ImmGenerateMessage(void)
|
|
{
|
|
const struct ime_call generate_sequence[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_TEST_WIN, .message = {.msg = WM_IME_COMPOSITION, .wparam = 0, .lparam = GCS_COMPSTR},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc,
|
|
.func = MSG_IME_UI, .message = {.msg = WM_IME_COMPOSITION, .wparam = 0, .lparam = GCS_COMPSTR},
|
|
},
|
|
{0},
|
|
};
|
|
TRANSMSG *msgs, *tmp_msgs;
|
|
INPUTCONTEXT *ctx;
|
|
HIMC himc;
|
|
HWND hwnd;
|
|
HKL hkl;
|
|
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
|
|
|
|
if (!(hkl = wineime_hkl)) return;
|
|
|
|
hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
ok_ret( 1, ImmLoadIME( hkl ) );
|
|
himc = ImmCreateContext();
|
|
ok_ne( NULL, himc, HIMC, "%p" );
|
|
ctx = ImmLockIMC( himc );
|
|
ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
|
|
process_messages();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
todo_wine ok_ret( 4, ImmGetIMCCSize( ctx->hMsgBuf ) );
|
|
ctx->hMsgBuf = ImmReSizeIMCC( ctx->hMsgBuf, sizeof(*msgs) );
|
|
ok_ne( NULL, ctx->hMsgBuf, HIMCC, "%p" );
|
|
|
|
msgs = ImmLockIMCC( ctx->hMsgBuf );
|
|
ok_ne( NULL, msgs, TRANSMSG *, "%p" );
|
|
msgs[0].message = WM_IME_COMPOSITION;
|
|
msgs[0].wParam = 0;
|
|
msgs[0].lParam = GCS_COMPSTR;
|
|
ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) );
|
|
|
|
ctx->hWnd = 0;
|
|
ctx->dwNumMsgBuf = 0;
|
|
ok_ret( 1, ImmGenerateMessage( himc ) );
|
|
ok_seq( empty_sequence );
|
|
ok_ret( sizeof(*msgs), ImmGetIMCCSize( ctx->hMsgBuf ) );
|
|
tmp_msgs = ImmLockIMCC( ctx->hMsgBuf );
|
|
ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" );
|
|
ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) );
|
|
|
|
ctx->dwNumMsgBuf = 1;
|
|
ok_ret( 1, ImmGenerateMessage( himc ) );
|
|
ok_seq( empty_sequence );
|
|
ok_eq( 0, ctx->dwNumMsgBuf, UINT, "%u" );
|
|
ok_ret( sizeof(*msgs), ImmGetIMCCSize( ctx->hMsgBuf ) );
|
|
tmp_msgs = ImmLockIMCC( ctx->hMsgBuf );
|
|
ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" );
|
|
ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) );
|
|
|
|
ctx->hWnd = hwnd;
|
|
ctx->dwNumMsgBuf = 0;
|
|
ok_ret( 1, ImmGenerateMessage( himc ) );
|
|
ok_seq( empty_sequence );
|
|
ok_ret( sizeof(*msgs), ImmGetIMCCSize( ctx->hMsgBuf ) );
|
|
tmp_msgs = ImmLockIMCC( ctx->hMsgBuf );
|
|
ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" );
|
|
ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) );
|
|
|
|
ctx->dwNumMsgBuf = 1;
|
|
ok_ret( 1, ImmGenerateMessage( himc ) );
|
|
ok_seq( generate_sequence );
|
|
ok_eq( 0, ctx->dwNumMsgBuf, UINT, "%u" );
|
|
ok_ret( sizeof(*msgs), ImmGetIMCCSize( ctx->hMsgBuf ) );
|
|
tmp_msgs = ImmLockIMCC( ctx->hMsgBuf );
|
|
ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" );
|
|
ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) );
|
|
|
|
tmp_msgs = ImmLockIMCC( ctx->hMsgBuf );
|
|
ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" );
|
|
ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) );
|
|
|
|
ok_ret( 1, ImmUnlockIMC( himc ) );
|
|
ok_ret( 1, ImmDestroyContext( himc ) );
|
|
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
ok_ret( 1, DestroyWindow( hwnd ) );
|
|
process_messages();
|
|
|
|
ok_ret( 1, ImmFreeLayout( hkl ) );
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
}
|
|
|
|
static void test_ImmTranslateMessage( BOOL kbd_char_first )
|
|
{
|
|
const struct ime_call process_key_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc, .func = IME_PROCESS_KEY,
|
|
.process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0x10)},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc, .func = IME_PROCESS_KEY,
|
|
.process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0xc010)},
|
|
},
|
|
{0},
|
|
};
|
|
const struct ime_call to_ascii_ex_0[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc, .func = IME_TO_ASCII_EX,
|
|
.to_ascii_ex = {.vkey = kbd_char_first ? MAKELONG('Q', 'q') : 'Q', .vsc = 0x10},
|
|
},
|
|
{0},
|
|
};
|
|
const struct ime_call to_ascii_ex_1[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc, .func = IME_PROCESS_KEY,
|
|
.process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0xc010)},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = default_himc, .func = IME_TO_ASCII_EX,
|
|
/* FIXME what happened to kbd_char_first here!? */
|
|
.to_ascii_ex = {.vkey = 'Q', .vsc = 0xc010},
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call to_ascii_ex_2[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/, .func = IME_PROCESS_KEY,
|
|
.process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0x210)},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/, .func = IME_TO_ASCII_EX,
|
|
.to_ascii_ex = {.vkey = kbd_char_first ? MAKELONG('Q', 'q') : 'Q', .vsc = 0x210},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/, .func = IME_PROCESS_KEY,
|
|
.process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0x410)},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/, .func = IME_TO_ASCII_EX,
|
|
.to_ascii_ex = {.vkey = kbd_char_first ? MAKELONG('Q', 'q') : 'Q', .vsc = 0x410},
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call to_ascii_ex_3[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/, .func = IME_PROCESS_KEY,
|
|
.process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0xa10)},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/, .func = IME_TO_ASCII_EX,
|
|
.to_ascii_ex = {.vkey = kbd_char_first ? MAKELONG('Q', 'q') : 'Q', .vsc = 0xa10},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/, .func = IME_PROCESS_KEY,
|
|
.process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0xc10)},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/, .func = IME_TO_ASCII_EX,
|
|
.to_ascii_ex = {.vkey = kbd_char_first ? MAKELONG('Q', 'q') : 'Q', .vsc = 0xc10},
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call key_down_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/, .func = IME_PROCESS_KEY,
|
|
.process_key = {.vkey = VK_RETURN, .lparam = MAKELONG(1, 0x1c)},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/, .func = IME_TO_ASCII_EX,
|
|
.to_ascii_ex = {.vkey = kbd_char_first ? MAKELONG(VK_RETURN, VK_RETURN) : VK_RETURN, .vsc = 0x1c},
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call key_up_seq[] =
|
|
{
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/, .func = IME_PROCESS_KEY,
|
|
.process_key = {.vkey = VK_RETURN, .lparam = MAKELONG(1, 0xc01c)},
|
|
},
|
|
{
|
|
.hkl = expect_ime, .himc = 0/*himc*/, .func = IME_TO_ASCII_EX,
|
|
.to_ascii_ex = {.vkey = VK_RETURN, .vsc = 0xc01c},
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call post_messages[] =
|
|
{
|
|
{.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 1}},
|
|
{.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_IME_UI, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 1},
|
|
.todo_himc = TRUE /* on some Wine configurations the IME UI doesn't get an HIMC */
|
|
},
|
|
{.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 1}},
|
|
{.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_IME_UI, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 1},
|
|
.todo_himc = TRUE /* on some Wine configurations the IME UI doesn't get an HIMC */
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call sent_messages[] =
|
|
{
|
|
{.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 2}},
|
|
{.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_IME_UI, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 2},
|
|
.todo_himc = TRUE /* on some Wine configurations the IME UI doesn't get an HIMC */
|
|
},
|
|
{.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 2}},
|
|
{.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_IME_UI, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 2},
|
|
.todo_himc = TRUE /* on some Wine configurations the IME UI doesn't get an HIMC */
|
|
},
|
|
{0},
|
|
};
|
|
HWND hwnd, other_hwnd;
|
|
INPUTCONTEXT *ctx;
|
|
HIMC himc;
|
|
HKL hkl;
|
|
UINT i;
|
|
|
|
ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE;
|
|
if (kbd_char_first) ime_info.fdwProperty |= IME_PROP_KBD_CHAR_FIRST;
|
|
|
|
winetest_push_context( kbd_char_first ? "kbd_char_first" : "default" );
|
|
|
|
if (!(hkl = wineime_hkl)) goto cleanup;
|
|
|
|
other_hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!other_hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
flush_events();
|
|
|
|
hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
flush_events();
|
|
|
|
ok_ret( 1, ImmActivateLayout( hkl ) );
|
|
ok_ret( 1, ImmLoadIME( hkl ) );
|
|
himc = ImmCreateContext();
|
|
ok_ne( NULL, himc, HIMC, "%p" );
|
|
ctx = ImmLockIMC( himc );
|
|
ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
|
|
process_messages();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0x10), 0 ) );
|
|
ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0xc010), 0 ) );
|
|
|
|
ok_ret( 0, ImmTranslateMessage( hwnd, 0, 0, 0 ) );
|
|
ok_ret( 'Q', ImmGetVirtualKey( hwnd ) );
|
|
ok_seq( process_key_seq );
|
|
|
|
ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYDOWN, 'Q', MAKELONG(2, 0x10) ) );
|
|
ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( hwnd ) );
|
|
ok_seq( to_ascii_ex_0 );
|
|
|
|
ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0xc010) ) );
|
|
ok_seq( empty_sequence );
|
|
|
|
ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0xc010), 0 ) );
|
|
ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0xc010) ) );
|
|
ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( hwnd ) );
|
|
ok_seq( to_ascii_ex_1 );
|
|
|
|
ok_eq( default_himc, ImmAssociateContext( hwnd, himc ), HIMC, "%p" );
|
|
ok_eq( default_himc, ImmAssociateContext( other_hwnd, himc ), HIMC, "%p" );
|
|
for (i = 0; i < ARRAY_SIZE(to_ascii_ex_2); i++) to_ascii_ex_2[i].himc = himc;
|
|
for (i = 0; i < ARRAY_SIZE(to_ascii_ex_3); i++) to_ascii_ex_3[i].himc = himc;
|
|
for (i = 0; i < ARRAY_SIZE(post_messages); i++) post_messages[i].himc = himc;
|
|
for (i = 0; i < ARRAY_SIZE(sent_messages); i++) sent_messages[i].himc = himc;
|
|
for (i = 0; i < ARRAY_SIZE(key_down_seq); i++) key_down_seq[i].himc = himc;
|
|
for (i = 0; i < ARRAY_SIZE(key_up_seq); i++) key_up_seq[i].himc = himc;
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
ctx->hWnd = hwnd;
|
|
ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0x210), 0 ) );
|
|
ok_ret( 1, ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0x210) ) );
|
|
ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0x410), 0 ) );
|
|
ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0x410) ) );
|
|
ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( hwnd ) );
|
|
ok_seq( to_ascii_ex_2 );
|
|
process_messages();
|
|
ok_seq( post_messages );
|
|
ok_ret( 1, ImmGenerateMessage( himc ) );
|
|
ok_seq( sent_messages );
|
|
|
|
ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0xa10), 0 ) );
|
|
ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0xa10) ) );
|
|
ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0xc10), 0 ) );
|
|
ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0xc10) ) );
|
|
ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( hwnd ) );
|
|
ok_seq( to_ascii_ex_3 );
|
|
process_messages();
|
|
ok_seq( empty_sequence );
|
|
ok_ret( 1, ImmGenerateMessage( himc ) );
|
|
ok_seq( sent_messages );
|
|
|
|
ctx->hWnd = 0;
|
|
ok_ret( 2, ImmProcessKey( other_hwnd, expect_ime, 'Q', MAKELONG(2, 0x210), 0 ) );
|
|
ok_ret( 1, ImmTranslateMessage( other_hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0x210) ) );
|
|
ok_ret( 2, ImmProcessKey( other_hwnd, expect_ime, 'Q', MAKELONG(2, 0x410), 0 ) );
|
|
ok_ret( 0, ImmTranslateMessage( other_hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0x410) ) );
|
|
ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( other_hwnd ) );
|
|
ok_seq( to_ascii_ex_2 );
|
|
process_messages_( hwnd );
|
|
ok_seq( empty_sequence );
|
|
process_messages_( other_hwnd );
|
|
ok_seq( post_messages );
|
|
ok_ret( 1, ImmGenerateMessage( himc ) );
|
|
ok_seq( empty_sequence );
|
|
|
|
|
|
ignore_WM_IME_NOTIFY = TRUE;
|
|
ignore_IME_NOTIFY = TRUE;
|
|
|
|
keybd_event( VK_RETURN, 0x1c, 0, 0 );
|
|
flush_events();
|
|
process_messages_( hwnd );
|
|
ok_seq( key_down_seq );
|
|
|
|
keybd_event( VK_RETURN, 0x1c, KEYEVENTF_KEYUP, 0 );
|
|
flush_events();
|
|
process_messages_( hwnd );
|
|
ok_seq( key_up_seq );
|
|
|
|
ignore_WM_IME_NOTIFY = FALSE;
|
|
ignore_IME_NOTIFY = FALSE;
|
|
|
|
|
|
ok_ret( 1, ImmUnlockIMC( himc ) );
|
|
ok_ret( 1, ImmDestroyContext( himc ) );
|
|
|
|
ok_ret( 1, ImmActivateLayout( default_hkl ) );
|
|
ok_ret( 1, DestroyWindow( other_hwnd ) );
|
|
ok_ret( 1, DestroyWindow( hwnd ) );
|
|
process_messages();
|
|
|
|
ok_ret( 1, ImmFreeLayout( hkl ) );
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
cleanup:
|
|
winetest_pop_context();
|
|
}
|
|
|
|
static void test_ga_na_da(void)
|
|
{
|
|
/* These sequences have some additional WM_IME_NOTIFY messages with unknown wparam > IMN_PRIVATE */
|
|
struct ime_call complete_seq[] =
|
|
{
|
|
/* G */
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 0, .lparam = 0}},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u3131", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x3131, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x1b, .lparam = GCS_CURSORPOS|GCS_DELTASTART|GCS_COMPSTR|GCS_COMPATTR|GCS_COMPCLAUSE|
|
|
GCS_COMPREADSTR|GCS_COMPREADATTR|GCS_COMPREADCLAUSE},
|
|
},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 0, .lparam = 0}},
|
|
|
|
/* G */
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 0, .lparam = 0}},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u3131", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x3131, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
/* A */
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\uac00", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xac00, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
/* N */
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\uac04", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xac04, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
/* A */
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub098", .result = L"\uac00",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xac00, .lparam = GCS_RESULTSTR},
|
|
},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0xac00, .lparam = 0x1}},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub098", .result = L"\uac00",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb098, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
/* D */
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub09f", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb09f, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
/* A */
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub2e4", .result = L"\ub098",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb098, .lparam = GCS_RESULTSTR},
|
|
},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0xb098, .lparam = 0x1}},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub2e4", .result = L"\ub098",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb2e4, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
/* RETURN */
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 0, .lparam = 0}},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"", .result = L"\ub2e4",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb2e4, .lparam = GCS_RESULTSTR},
|
|
},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0xb2e4, .lparam = 0x1}},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_KEYDOWN, .wparam = 0xd, .lparam = 0x1c0001}},
|
|
{0},
|
|
};
|
|
struct ime_call partial_g_seq[] =
|
|
{
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 0, .lparam = 0}},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u3131", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x3131, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call partial_ga_seq[] =
|
|
{
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\uac00", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xac00, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call partial_n_seq[] =
|
|
{
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\uac04", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xac04, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call partial_na_seq[] =
|
|
{
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub098", .result = L"\uac00",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xac00, .lparam = GCS_RESULTSTR},
|
|
},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0xac00, .lparam = 0x1}},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub098", .result = L"\uac00",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb098, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call partial_d_seq[] =
|
|
{
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub09f", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb09f, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call partial_da_seq[] =
|
|
{
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub2e4", .result = L"\ub098",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb098, .lparam = GCS_RESULTSTR},
|
|
},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0xb098, .lparam = 0x1}},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub2e4", .result = L"\ub098",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb2e4, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
{0},
|
|
};
|
|
struct ime_call partial_return_seq[] =
|
|
{
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 0, .lparam = 0}},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"", .result = L"\ub2e4",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb2e4, .lparam = GCS_RESULTSTR},
|
|
},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0xb2e4, .lparam = 0x1}},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_KEYDOWN, .wparam = 0xd, .lparam = 0x1c0001}},
|
|
{0},
|
|
};
|
|
struct ime_call cancel_seq[] =
|
|
{
|
|
/* G */
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 0, .lparam = 0}},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u3131", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x3131, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
/* A */
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\uac00", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xac00, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
/* N */
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\uac04", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xac04, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
/* A */
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub098", .result = L"\uac00",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xac00, .lparam = GCS_RESULTSTR},
|
|
},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0xac00, .lparam = 0x1}},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub098", .result = L"\uac00",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb098, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
|
|
/* CPS_CANCEL */
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 0, .lparam = 0}},
|
|
{0},
|
|
};
|
|
struct ime_call closed_seq[] =
|
|
{
|
|
/* G */
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 0, .lparam = 0}},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u3131", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x3131, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
/* A */
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\uac00", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xac00, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
/* N */
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\uac04", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xac04, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
/* A */
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub098", .result = L"\uac00",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xac00, .lparam = GCS_RESULTSTR},
|
|
},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0xac00, .lparam = 0x1}},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub098", .result = L"\uac00",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb098, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET},
|
|
},
|
|
|
|
/* CPS_COMPLETE */
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 0, .lparam = 0}},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"", .result = L"\ub098",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb098, .lparam = GCS_RESULTSTR},
|
|
},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0xb098, .lparam = 0x1}},
|
|
{0},
|
|
};
|
|
|
|
INPUTCONTEXT *ctx;
|
|
HWND hwnd;
|
|
HIMC himc;
|
|
UINT i;
|
|
|
|
/* this test doesn't work on Win32 / WoW64 */
|
|
if (broken(sizeof(void *) == 4) || default_hkl != (HKL)0x04120412 /* MS Korean IME */)
|
|
{
|
|
skip( "Got hkl %p, skipping Korean IME-specific test\n", default_hkl );
|
|
process_messages();
|
|
return;
|
|
}
|
|
|
|
hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
flush_events();
|
|
|
|
ignore_WM_IME_NOTIFY = TRUE;
|
|
ignore_IME_NOTIFY = TRUE;
|
|
|
|
himc = ImmCreateContext();
|
|
ok_ne( NULL, himc, HIMC, "%p" );
|
|
ctx = ImmLockIMC( himc );
|
|
ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
|
|
ok_eq( default_himc, ImmAssociateContext( hwnd, himc ), HIMC, "%p" );
|
|
ok_ret( 1, ImmSetOpenStatus( himc, TRUE ) );
|
|
ok_ret( 1, ImmSetConversionStatus( himc, IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE, IME_SMODE_PHRASEPREDICT ) );
|
|
flush_events();
|
|
|
|
keybd_event( 'R', 0x13, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'R', 0x13, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( VK_RETURN, 0x1c, 0, 0 );
|
|
flush_events();
|
|
keybd_event( VK_RETURN, 0x1c, KEYEVENTF_KEYUP, 0 );
|
|
flush_events();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(complete_seq); i++) complete_seq[i].himc = himc;
|
|
for (i = 0; i < ARRAY_SIZE(partial_g_seq); i++) partial_g_seq[i].himc = himc;
|
|
for (i = 0; i < ARRAY_SIZE(partial_ga_seq); i++) partial_ga_seq[i].himc = himc;
|
|
for (i = 0; i < ARRAY_SIZE(partial_n_seq); i++) partial_n_seq[i].himc = himc;
|
|
for (i = 0; i < ARRAY_SIZE(partial_na_seq); i++) partial_na_seq[i].himc = himc;
|
|
for (i = 0; i < ARRAY_SIZE(partial_d_seq); i++) partial_d_seq[i].himc = himc;
|
|
for (i = 0; i < ARRAY_SIZE(partial_da_seq); i++) partial_da_seq[i].himc = himc;
|
|
for (i = 0; i < ARRAY_SIZE(partial_return_seq); i++) partial_return_seq[i].himc = himc;
|
|
for (i = 0; i < ARRAY_SIZE(cancel_seq); i++) cancel_seq[i].himc = himc;
|
|
for (i = 0; i < ARRAY_SIZE(closed_seq); i++) closed_seq[i].himc = himc;
|
|
|
|
keybd_event( 'R', 0x13, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'R', 0x13, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( VK_BACK, 0x0e, 0, 0 );
|
|
flush_events();
|
|
keybd_event( VK_BACK, 0x0e, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'R', 0x13, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'R', 0x13, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'K', 0x25, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'K', 0x25, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'S', 0x1f, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'S', 0x1f, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'K', 0x25, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'K', 0x25, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'E', 0x12, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'E', 0x12, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'K', 0x25, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'K', 0x25, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( VK_RETURN, 0x1c, 0, 0 );
|
|
flush_events();
|
|
keybd_event( VK_RETURN, 0x1c, KEYEVENTF_KEYUP, 0 );
|
|
|
|
flush_events();
|
|
todo_wine ok_seq( complete_seq );
|
|
|
|
|
|
/* Korean IME uses ImeProcessKey and posts messages */
|
|
|
|
todo_wine ok_ret( 2, ImmProcessKey( hwnd, default_hkl, 'R', MAKELONG(1, 0x13), 0 ) );
|
|
ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYDOWN, 'R', MAKELONG(1, 0x13) ) );
|
|
ok_seq( empty_sequence );
|
|
process_messages();
|
|
todo_wine ok_seq( partial_g_seq );
|
|
|
|
/* Korean IME doesn't eat WM_KEYUP */
|
|
|
|
ok_ret( 0, ImmProcessKey( hwnd, default_hkl, 'R', MAKELONG(1, 0xc013), 0 ) );
|
|
ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'R', MAKELONG(1, 0xc013) ) );
|
|
process_messages();
|
|
ok_seq( empty_sequence );
|
|
|
|
todo_wine ok_ret( 2, ImmProcessKey( hwnd, default_hkl, 'K', MAKELONG(1, 0x25), 0 ) );
|
|
ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYDOWN, 'K', MAKELONG(1, 0x25) ) );
|
|
process_messages();
|
|
todo_wine ok_seq( partial_ga_seq );
|
|
|
|
todo_wine ok_ret( 2, ImmProcessKey( hwnd, default_hkl, 'S', MAKELONG(1, 0x1f), 0 ) );
|
|
ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYDOWN, 'S', MAKELONG(1, 0x1f) ) );
|
|
process_messages();
|
|
todo_wine ok_seq( partial_n_seq );
|
|
|
|
todo_wine ok_ret( 2, ImmProcessKey( hwnd, default_hkl, 'K', MAKELONG(1, 0x25), 0 ) );
|
|
ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYDOWN, 'K', MAKELONG(1, 0x25) ) );
|
|
process_messages();
|
|
todo_wine ok_seq( partial_na_seq );
|
|
|
|
todo_wine ok_ret( 2, ImmProcessKey( hwnd, default_hkl, 'E', MAKELONG(1, 0x12), 0 ) );
|
|
ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYDOWN, 'E', MAKELONG(1, 0x12) ) );
|
|
process_messages();
|
|
todo_wine ok_seq( partial_d_seq );
|
|
|
|
todo_wine ok_ret( 2, ImmProcessKey( hwnd, default_hkl, 'K', MAKELONG(1, 0x25), 0 ) );
|
|
ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYDOWN, 'K', MAKELONG(1, 0x25) ) );
|
|
process_messages();
|
|
todo_wine ok_seq( partial_da_seq );
|
|
|
|
todo_wine ok_ret( 2, ImmProcessKey( hwnd, default_hkl, VK_RETURN, MAKELONG(1, 0x1c), 0 ) );
|
|
ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYDOWN, VK_RETURN, MAKELONG(1, 0x1c) ) );
|
|
process_messages();
|
|
todo_wine ok_seq( partial_return_seq );
|
|
|
|
|
|
/* cancelling clears the composition string */
|
|
|
|
keybd_event( 'R', 0x13, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'R', 0x13, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'K', 0x25, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'K', 0x25, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'S', 0x1f, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'S', 0x1f, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'K', 0x25, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'K', 0x25, KEYEVENTF_KEYUP, 0 );
|
|
|
|
ok_ret( 1, ImmNotifyIME( himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0 ) );
|
|
|
|
flush_events();
|
|
todo_wine ok_seq( cancel_seq );
|
|
|
|
|
|
/* CPS_COMPLETE and ImmSetOpenStatus( himc, FALSE ) do the same thing */
|
|
|
|
keybd_event( 'R', 0x13, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'R', 0x13, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'K', 0x25, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'K', 0x25, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'S', 0x1f, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'S', 0x1f, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'K', 0x25, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'K', 0x25, KEYEVENTF_KEYUP, 0 );
|
|
|
|
ok_ret( 1, ImmNotifyIME( himc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0 ) );
|
|
|
|
flush_events();
|
|
todo_wine ok_seq( closed_seq );
|
|
|
|
|
|
keybd_event( 'R', 0x13, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'R', 0x13, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'K', 0x25, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'K', 0x25, KEYEVENTF_KEYUP, 0 );
|
|
|
|
/* does nothing, already open */
|
|
ok_ret( 1, ImmSetOpenStatus( himc, TRUE ) );
|
|
|
|
keybd_event( 'S', 0x1f, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'S', 0x1f, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'K', 0x25, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'K', 0x25, KEYEVENTF_KEYUP, 0 );
|
|
|
|
ok_ret( 1, ImmSetOpenStatus( himc, FALSE ) );
|
|
|
|
flush_events();
|
|
todo_wine ok_seq( closed_seq );
|
|
|
|
ignore_WM_IME_NOTIFY = FALSE;
|
|
ignore_IME_NOTIFY = FALSE;
|
|
|
|
|
|
|
|
ok_ret( 1, ImmSetConversionStatus( himc, 0, IME_SMODE_PHRASEPREDICT ) );
|
|
ok_ret( 1, ImmSetOpenStatus( himc, FALSE ) );
|
|
|
|
ok_ret( 1, ImmUnlockIMC( himc ) );
|
|
ok_ret( 1, ImmDestroyContext( himc ) );
|
|
|
|
ok_ret( 1, DestroyWindow( hwnd ) );
|
|
process_messages();
|
|
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
}
|
|
|
|
static void test_nihongo_no(void)
|
|
{
|
|
/* These sequences have some additional WM_IME_NOTIFY messages with wparam > IMN_PRIVATE */
|
|
/* Some out-of-order WM_IME_REQUEST and WM_IME_NOTIFY messages are also ignored */
|
|
struct ime_call complete_seq[] =
|
|
{
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 0, .lparam = 0}},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\uff4e", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xff4e, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS},
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u306b", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306b, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS},
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u306b\uff48", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306b, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS},
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u306b\u307b", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306b, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS},
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u306b\u307b\uff4e", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306b, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS},
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u306b\u307b\u3093\uff47", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306b, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS},
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u306b\u307b\u3093\u3054", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306b, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS},
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u65e5\u672c\u8a9e", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x65e5, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS},
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"", .result = L"\u65e5\u672c\u8a9e",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x65e5, .lparam = GCS_RESULTSTR|GCS_RESULTCLAUSE|GCS_RESULTREADSTR|GCS_RESULTREADCLAUSE},
|
|
},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0x65e5, .lparam = 0x1}},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0x672c, .lparam = 0x1}},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0x8a9e, .lparam = 0x1}},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 0, .lparam = 0}},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 0, .lparam = 0}},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\uff4e", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xff4e, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS},
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u306e", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306e, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS},
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"", .result = L"\u306e",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306e, .lparam = GCS_RESULTSTR|GCS_RESULTCLAUSE|GCS_RESULTREADSTR|GCS_RESULTREADCLAUSE},
|
|
},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0x306e, .lparam = 0x1}},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 0, .lparam = 0}},
|
|
{0},
|
|
};
|
|
|
|
struct ime_call cancel_seq[] =
|
|
{
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 0, .lparam = 0}},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\uff4e", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xff4e, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS},
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u306b", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306b, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS},
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u306b\uff48", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306b, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS},
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u306b\u307b", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306b, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS},
|
|
},
|
|
|
|
/* CPS_CANCEL */
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 0, .lparam = 0}},
|
|
{0},
|
|
};
|
|
struct ime_call closed_seq[] =
|
|
{
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 0, .lparam = 0}},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\uff4e", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0xff4e, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS},
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u306b", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306b, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS},
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u306b\uff48", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306b, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS},
|
|
},
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u306b\u307b", .result = L"",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306b, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS},
|
|
},
|
|
|
|
/* CPS_COMPLETE */
|
|
{
|
|
.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"", .result = L"\u306b\u307b",
|
|
.message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306b, .lparam = GCS_RESULTSTR|GCS_RESULTCLAUSE|GCS_RESULTREADSTR|GCS_RESULTREADCLAUSE},
|
|
},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0x306b, .lparam = 1}},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0x307b, .lparam = 1}},
|
|
{.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 0, .lparam = 0}},
|
|
{0},
|
|
};
|
|
|
|
INPUTCONTEXT *ctx;
|
|
HWND hwnd;
|
|
HIMC himc;
|
|
UINT i;
|
|
|
|
/* this test doesn't work on Win32 / WoW64 */
|
|
if (broken(sizeof(void *) == 4) || default_hkl != (HKL)0x04110411 /* MS Japanese IME */)
|
|
{
|
|
skip( "Got hkl %p, skipping Japanese IME-specific test\n", default_hkl );
|
|
return;
|
|
}
|
|
|
|
hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
100, 100, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() );
|
|
flush_events();
|
|
|
|
himc = ImmCreateContext();
|
|
ok_ne( NULL, himc, HIMC, "%p" );
|
|
ctx = ImmLockIMC( himc );
|
|
ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" );
|
|
ok_eq( default_himc, ImmAssociateContext( hwnd, himc ), HIMC, "%p" );
|
|
ok_ret( 1, ImmSetOpenStatus( himc, TRUE ) );
|
|
ok_ret( 1, ImmSetConversionStatus( himc, IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE, IME_SMODE_PHRASEPREDICT ) );
|
|
flush_events();
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(complete_seq); i++) complete_seq[i].himc = himc;
|
|
for (i = 0; i < ARRAY_SIZE(cancel_seq); i++) cancel_seq[i].himc = himc;
|
|
for (i = 0; i < ARRAY_SIZE(closed_seq); i++) closed_seq[i].himc = himc;
|
|
ignore_WM_IME_REQUEST = TRUE;
|
|
ignore_WM_IME_NOTIFY = TRUE;
|
|
ignore_IME_NOTIFY = TRUE;
|
|
|
|
|
|
keybd_event( 'N', 0x31, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'N', 0x31, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'I', 0x17, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'I', 0x17, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'H', 0x23, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'H', 0x23, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'O', 0x18, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'O', 0x18, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'N', 0x31, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'N', 0x31, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'G', 0x22, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'G', 0x22, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'O', 0x18, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'O', 0x18, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( VK_SPACE, 0x39, 0, 0 );
|
|
flush_events();
|
|
keybd_event( VK_SPACE, 0x39, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'N', 0x31, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'N', 0x31, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'O', 0x18, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'O', 0x18, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( VK_RETURN, 0x1c, 0, 0 );
|
|
flush_events();
|
|
keybd_event( VK_RETURN, 0x1c, KEYEVENTF_KEYUP, 0 );
|
|
|
|
flush_events();
|
|
todo_wine ok_seq( complete_seq );
|
|
|
|
ignore_WM_IME_REQUEST = FALSE;
|
|
ignore_WM_IME_NOTIFY = FALSE;
|
|
ignore_IME_NOTIFY = FALSE;
|
|
|
|
/* Japanese IME doesn't take input from ImmProcessKey */
|
|
|
|
ok_ret( 0, ImmProcessKey( hwnd, default_hkl, 'N', MAKELONG(1, 0x31), 0 ) );
|
|
ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYDOWN, 'N', MAKELONG(1, 0x31) ) );
|
|
flush_events();
|
|
ok_ret( 0, ImmProcessKey( hwnd, default_hkl, 'N', MAKELONG(1, 0xc031), 0 ) );
|
|
ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'N', MAKELONG(1, 0xc031) ) );
|
|
flush_events();
|
|
ok_seq( empty_sequence );
|
|
|
|
|
|
/* changing the open status completes the composition string */
|
|
|
|
ignore_WM_IME_REQUEST = TRUE;
|
|
ignore_WM_IME_NOTIFY = TRUE;
|
|
ignore_IME_NOTIFY = TRUE;
|
|
|
|
|
|
/* cancelling clears the composition string */
|
|
|
|
keybd_event( 'N', 0x31, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'N', 0x31, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'I', 0x17, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'I', 0x17, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'H', 0x23, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'H', 0x23, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'O', 0x18, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'O', 0x18, KEYEVENTF_KEYUP, 0 );
|
|
|
|
ok_ret( 1, ImmNotifyIME( himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0 ) );
|
|
|
|
flush_events();
|
|
todo_wine ok_seq( cancel_seq );
|
|
|
|
|
|
/* CPS_COMPLETE and ImmSetOpenStatus( himc, FALSE ) do the same thing */
|
|
|
|
keybd_event( 'N', 0x31, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'N', 0x31, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'I', 0x17, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'I', 0x17, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'H', 0x23, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'H', 0x23, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'O', 0x18, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'O', 0x18, KEYEVENTF_KEYUP, 0 );
|
|
|
|
ok_ret( 1, ImmNotifyIME( himc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0 ) );
|
|
|
|
flush_events();
|
|
todo_wine ok_seq( closed_seq );
|
|
|
|
|
|
keybd_event( 'N', 0x31, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'N', 0x31, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'I', 0x17, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'I', 0x17, KEYEVENTF_KEYUP, 0 );
|
|
|
|
/* does nothing, already open */
|
|
ok_ret( 1, ImmSetOpenStatus( himc, TRUE ) );
|
|
|
|
keybd_event( 'H', 0x23, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'H', 0x23, KEYEVENTF_KEYUP, 0 );
|
|
|
|
keybd_event( 'O', 0x18, 0, 0 );
|
|
flush_events();
|
|
keybd_event( 'O', 0x18, KEYEVENTF_KEYUP, 0 );
|
|
|
|
ok_ret( 1, ImmSetOpenStatus( himc, FALSE ) );
|
|
|
|
flush_events();
|
|
todo_wine ok_seq( closed_seq );
|
|
|
|
ignore_WM_IME_REQUEST = FALSE;
|
|
ignore_WM_IME_NOTIFY = FALSE;
|
|
ignore_IME_NOTIFY = FALSE;
|
|
|
|
|
|
ok_ret( 1, ImmSetConversionStatus( himc, 0, IME_SMODE_PHRASEPREDICT ) );
|
|
ok_ret( 1, ImmSetOpenStatus( himc, FALSE ) );
|
|
|
|
ok_ret( 1, ImmUnlockIMC( himc ) );
|
|
ok_ret( 1, ImmDestroyContext( himc ) );
|
|
|
|
ok_ret( 1, DestroyWindow( hwnd ) );
|
|
process_messages();
|
|
|
|
memset( ime_calls, 0, sizeof(ime_calls) );
|
|
ime_call_count = 0;
|
|
}
|
|
|
|
static BOOL CALLBACK enum_first_current_thread_window_proc( HWND hwnd, LPARAM lparam )
|
|
{
|
|
if (GetWindowThreadProcessId( hwnd, NULL ) == GetCurrentThreadId())
|
|
{
|
|
*(HWND *)lparam = hwnd;
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void test_ime_ui_window_child( void )
|
|
{
|
|
HWND hwnd, result_hwnd = 0;
|
|
|
|
/* Unity expects the first window in the current thread to be its game window, not Wine IME */
|
|
hwnd = CreateWindowW( L"static", L"", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, NULL, NULL );
|
|
ok_ne( 0, hwnd, HWND, "%p" );
|
|
EnumWindows( enum_first_current_thread_window_proc, (LPARAM)&result_hwnd );
|
|
ok_eq( result_hwnd, hwnd, HWND, "%p" );
|
|
DestroyWindow( hwnd );
|
|
}
|
|
|
|
static void test_ime_ui_window( const char *argv0 )
|
|
{
|
|
PROCESS_INFORMATION info;
|
|
STARTUPINFOA startup;
|
|
char cmd[MAX_PATH];
|
|
|
|
/* Run in a new process to avoid interference from windows in the current process */
|
|
sprintf( cmd, "%s imm32 test_ime_ui_window_child", argv0 );
|
|
memset( &startup, 0, sizeof(startup) );
|
|
startup.cb = sizeof(startup);
|
|
CreateProcessA( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info );
|
|
|
|
wait_child_process( info.hProcess );
|
|
CloseHandle( info.hProcess );
|
|
CloseHandle( info.hThread );
|
|
}
|
|
|
|
START_TEST(imm32)
|
|
{
|
|
char **argv;
|
|
int argc;
|
|
|
|
argc = winetest_get_mainargs( &argv );
|
|
if (argc == 3 && !strcmp( argv[2], "test_ime_ui_window_child" ))
|
|
{
|
|
test_ime_ui_window_child();
|
|
return;
|
|
}
|
|
|
|
default_hkl = GetKeyboardLayout( 0 );
|
|
|
|
test_class.hInstance = GetModuleHandleW( NULL );
|
|
RegisterClassExW( &test_class );
|
|
|
|
if (!is_ime_enabled())
|
|
{
|
|
win_skip("IME support not implemented\n");
|
|
return;
|
|
}
|
|
|
|
test_ime_ui_window( argv[0] );
|
|
test_com_initialization();
|
|
|
|
test_ImmEnumInputContext();
|
|
|
|
/* run these before installing the custom IME, sometimes it takes a moment
|
|
* to uninstall and the default IME doesn't activate immediately
|
|
*/
|
|
test_ga_na_da();
|
|
test_nihongo_no();
|
|
|
|
test_ImmInstallIME();
|
|
wineime_hkl = ime_install();
|
|
|
|
test_ImmGetDescription();
|
|
test_ImmGetIMEFileName();
|
|
test_ImmIsIME();
|
|
test_ImmGetProperty();
|
|
|
|
test_ImmEscape( FALSE );
|
|
test_ImmEscape( TRUE );
|
|
test_ImmEnumRegisterWord( FALSE );
|
|
test_ImmEnumRegisterWord( TRUE );
|
|
test_ImmRegisterWord( FALSE );
|
|
test_ImmRegisterWord( TRUE );
|
|
test_ImmGetRegisterWordStyle( FALSE );
|
|
test_ImmGetRegisterWordStyle( TRUE );
|
|
test_ImmUnregisterWord( FALSE );
|
|
test_ImmUnregisterWord( TRUE );
|
|
|
|
/* test these first to sanitize conversion / open statuses */
|
|
test_ImmSetConversionStatus();
|
|
test_ImmSetOpenStatus();
|
|
ImeSelect_init_status = TRUE;
|
|
|
|
test_ImmActivateLayout();
|
|
test_ImmCreateInputContext();
|
|
test_ImmProcessKey();
|
|
test_DefWindowProc();
|
|
test_ImmSetActiveContext();
|
|
test_ImmRequestMessage();
|
|
|
|
test_ImmGetCandidateList( TRUE );
|
|
test_ImmGetCandidateList( FALSE );
|
|
test_ImmGetCandidateListCount( TRUE );
|
|
test_ImmGetCandidateListCount( FALSE );
|
|
test_ImmGetCandidateWindow();
|
|
|
|
test_ImmGetCompositionString( TRUE );
|
|
test_ImmGetCompositionString( FALSE );
|
|
test_ImmSetCompositionWindow();
|
|
test_ImmSetStatusWindowPos();
|
|
test_ImmSetCompositionFont( TRUE );
|
|
test_ImmSetCompositionFont( FALSE );
|
|
test_ImmSetCandidateWindow();
|
|
|
|
test_ImmGenerateMessage();
|
|
test_ImmTranslateMessage( FALSE );
|
|
test_ImmTranslateMessage( TRUE );
|
|
|
|
if (wineime_hkl) ime_cleanup( wineime_hkl, TRUE );
|
|
|
|
if (init())
|
|
{
|
|
test_ImmNotifyIME();
|
|
test_SCS_SETSTR();
|
|
test_ImmIME();
|
|
test_ImmAssociateContextEx();
|
|
test_NtUserAssociateInputContext();
|
|
test_cross_thread_himc();
|
|
test_ImmIsUIMessage();
|
|
test_ImmGetContext();
|
|
test_ImmDefaultHwnd();
|
|
test_default_ime_window_creation();
|
|
test_ImmGetIMCLockCount();
|
|
test_ImmGetIMCCLockCount();
|
|
test_ImmDestroyContext();
|
|
test_ImmDestroyIMCC();
|
|
test_InvalidIMC();
|
|
msg_spy_cleanup();
|
|
/* Reinitialize the hooks to capture all windows */
|
|
msg_spy_init(NULL);
|
|
test_ImmMessages();
|
|
msg_spy_cleanup();
|
|
if (pSendInput)
|
|
test_ime_processkey();
|
|
else win_skip("SendInput is not available\n");
|
|
|
|
/* there's no way of enabling IME - keep the test last */
|
|
test_ImmDisableIME();
|
|
}
|
|
cleanup();
|
|
|
|
UnregisterClassW( test_class.lpszClassName, test_class.hInstance );
|
|
}
|