https://git.reactos.org/?p=reactos.git;a=commitdiff;h=bd0629905963e0002ef778cd3b26ebcd95185dbb

commit bd0629905963e0002ef778cd3b26ebcd95185dbb
Author:     Katayama Hirofumi MZ <[email protected]>
AuthorDate: Fri Nov 24 23:02:19 2023 +0900
Commit:     GitHub <[email protected]>
CommitDate: Fri Nov 24 23:02:19 2023 +0900

    [IMM32][SDK] Implement IME Soft Keyboard Type T1 (#6021)
    
    - Rename version.rc as imm32.rc.
    - Add resource.h and t1keys.h.
    - Add some resource bitmaps.
    - Modify <immdev.h>.
    
    Test:
    Press Ctrl+Alt+comma on FreeCJ2004.
    
    NOTE: There's a visual bug in PatBlt with negative values.
    CORE-19268
---
 dll/win32/imm32/CMakeLists.txt           |   23 +-
 dll/win32/imm32/{version.rc => imm32.rc} |   26 +
 dll/win32/imm32/res/1033_Bitmap_100.bmp  |  Bin 0 -> 190 bytes
 dll/win32/imm32/res/1033_Bitmap_101.bmp  |  Bin 0 -> 190 bytes
 dll/win32/imm32/res/1033_Bitmap_102.bmp  |  Bin 0 -> 226 bytes
 dll/win32/imm32/res/1033_Bitmap_103.bmp  |  Bin 0 -> 262 bytes
 dll/win32/imm32/res/1033_Bitmap_104.bmp  |  Bin 0 -> 226 bytes
 dll/win32/imm32/res/1033_Bitmap_105.bmp  |  Bin 0 -> 190 bytes
 dll/win32/imm32/res/1033_Bitmap_106.bmp  |  Bin 0 -> 226 bytes
 dll/win32/imm32/res/1033_Bitmap_107.bmp  |  Bin 0 -> 190 bytes
 dll/win32/imm32/res/1033_Bitmap_108.bmp  |  Bin 0 -> 446 bytes
 dll/win32/imm32/res/2052_Bitmap_201.bmp  |  Bin 0 -> 502 bytes
 dll/win32/imm32/res/2052_Bitmap_202.bmp  |  Bin 0 -> 502 bytes
 dll/win32/imm32/res/2052_Bitmap_203.bmp  |  Bin 0 -> 598 bytes
 dll/win32/imm32/res/2052_Bitmap_204.bmp  |  Bin 0 -> 598 bytes
 dll/win32/imm32/res/2052_Bitmap_205.bmp  |  Bin 0 -> 790 bytes
 dll/win32/imm32/res/2052_Bitmap_206.bmp  |  Bin 0 -> 518 bytes
 dll/win32/imm32/res/2052_Bitmap_207.bmp  |  Bin 0 -> 518 bytes
 dll/win32/imm32/res/2052_Bitmap_208.bmp  |  Bin 0 -> 518 bytes
 dll/win32/imm32/res/2052_Bitmap_209.bmp  |  Bin 0 -> 446 bytes
 dll/win32/imm32/resource.h               |   19 +
 dll/win32/imm32/softkbd.c                | 1260 +++++++++++++++++++++++++++---
 dll/win32/imm32/t1keys.h                 |   68 ++
 sdk/include/ddk/immdev.h                 |   16 +-
 24 files changed, 1295 insertions(+), 117 deletions(-)

diff --git a/dll/win32/imm32/CMakeLists.txt b/dll/win32/imm32/CMakeLists.txt
index d5b2ea12af4..b765ec172ab 100644
--- a/dll/win32/imm32/CMakeLists.txt
+++ b/dll/win32/imm32/CMakeLists.txt
@@ -22,8 +22,29 @@ list(APPEND SOURCE
     ${CMAKE_CURRENT_BINARY_DIR}/imm32_stubs.c
     ${CMAKE_CURRENT_BINARY_DIR}/imm32.def)
 
-add_library(imm32 MODULE ${SOURCE} version.rc)
+list(APPEND imm32_rc_deps
+    ${CMAKE_CURRENT_SOURCE_DIR}/res/1033_Bitmap_100.bmp
+    ${CMAKE_CURRENT_SOURCE_DIR}/res/1033_Bitmap_101.bmp
+    ${CMAKE_CURRENT_SOURCE_DIR}/res/1033_Bitmap_102.bmp
+    ${CMAKE_CURRENT_SOURCE_DIR}/res/1033_Bitmap_103.bmp
+    ${CMAKE_CURRENT_SOURCE_DIR}/res/1033_Bitmap_104.bmp
+    ${CMAKE_CURRENT_SOURCE_DIR}/res/1033_Bitmap_105.bmp
+    ${CMAKE_CURRENT_SOURCE_DIR}/res/1033_Bitmap_106.bmp
+    ${CMAKE_CURRENT_SOURCE_DIR}/res/1033_Bitmap_107.bmp
+    ${CMAKE_CURRENT_SOURCE_DIR}/res/1033_Bitmap_108.bmp
+    ${CMAKE_CURRENT_SOURCE_DIR}/res/2052_Bitmap_201.bmp
+    ${CMAKE_CURRENT_SOURCE_DIR}/res/2052_Bitmap_202.bmp
+    ${CMAKE_CURRENT_SOURCE_DIR}/res/2052_Bitmap_203.bmp
+    ${CMAKE_CURRENT_SOURCE_DIR}/res/2052_Bitmap_204.bmp
+    ${CMAKE_CURRENT_SOURCE_DIR}/res/2052_Bitmap_205.bmp
+    ${CMAKE_CURRENT_SOURCE_DIR}/res/2052_Bitmap_206.bmp
+    ${CMAKE_CURRENT_SOURCE_DIR}/res/2052_Bitmap_207.bmp
+    ${CMAKE_CURRENT_SOURCE_DIR}/res/2052_Bitmap_208.bmp
+    ${CMAKE_CURRENT_SOURCE_DIR}/res/2052_Bitmap_209.bmp)
+
+add_library(imm32 MODULE ${SOURCE} imm32.rc)
 set_module_type(imm32 win32dll UNICODE ENTRYPOINT ImmDllInitialize 12)
+set_source_files_properties(imm32.rc PROPERTIES OBJECT_DEPENDS 
"${imm32_rc_deps}")
 target_link_libraries(imm32 wine win32ksys)
 add_importlibs(imm32 advapi32 user32 gdi32 kernel32 ntdll)
 add_cd_file(TARGET imm32 DESTINATION reactos/system32 FOR all)
diff --git a/dll/win32/imm32/version.rc b/dll/win32/imm32/imm32.rc
similarity index 51%
rename from dll/win32/imm32/version.rc
rename to dll/win32/imm32/imm32.rc
index ae821025c01..317f986b7f2 100644
--- a/dll/win32/imm32/version.rc
+++ b/dll/win32/imm32/imm32.rc
@@ -24,3 +24,29 @@
 #define WINE_PRODUCTVERSION_STR "5.1.2600.2180"
 
 #include "wine/wine_common_ver.rc"
+
+#include "resource.h"
+
+LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL
+
+IDB_T1_BACKSPACE BITMAP "res/1033_Bitmap_100.bmp"
+IDB_T1_TAB       BITMAP "res/1033_Bitmap_101.bmp"
+IDB_T1_CAPS      BITMAP "res/1033_Bitmap_102.bmp"
+IDB_T1_ENTER     BITMAP "res/1033_Bitmap_103.bmp"
+IDB_T1_SHIFT     BITMAP "res/1033_Bitmap_104.bmp"
+IDB_T1_CTRL      BITMAP "res/1033_Bitmap_105.bmp"
+IDB_T1_ESCAPE    BITMAP "res/1033_Bitmap_106.bmp"
+IDB_T1_ALT       BITMAP "res/1033_Bitmap_107.bmp"
+IDB_T1_CHARS     BITMAP "res/1033_Bitmap_108.bmp"
+
+LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
+
+IDB_C1_BACKSPACE BITMAP "res/2052_Bitmap_201.bmp"
+IDB_C1_TAB       BITMAP "res/2052_Bitmap_202.bmp"
+IDB_C1_CAPS      BITMAP "res/2052_Bitmap_203.bmp"
+IDB_C1_ENTER     BITMAP "res/2052_Bitmap_204.bmp"
+IDB_C1_SHIFT     BITMAP "res/2052_Bitmap_205.bmp"
+IDB_C1_INS       BITMAP "res/2052_Bitmap_206.bmp"
+IDB_C1_DEL       BITMAP "res/2052_Bitmap_207.bmp"
+IDB_C1_ESCAPE    BITMAP "res/2052_Bitmap_208.bmp"
+IDB_C1_CHARS     BITMAP "res/2052_Bitmap_209.bmp"
diff --git a/dll/win32/imm32/res/1033_Bitmap_100.bmp 
b/dll/win32/imm32/res/1033_Bitmap_100.bmp
new file mode 100644
index 00000000000..79684fd689d
Binary files /dev/null and b/dll/win32/imm32/res/1033_Bitmap_100.bmp differ
diff --git a/dll/win32/imm32/res/1033_Bitmap_101.bmp 
b/dll/win32/imm32/res/1033_Bitmap_101.bmp
new file mode 100644
index 00000000000..25cbf71f15c
Binary files /dev/null and b/dll/win32/imm32/res/1033_Bitmap_101.bmp differ
diff --git a/dll/win32/imm32/res/1033_Bitmap_102.bmp 
b/dll/win32/imm32/res/1033_Bitmap_102.bmp
new file mode 100644
index 00000000000..063dbf4361b
Binary files /dev/null and b/dll/win32/imm32/res/1033_Bitmap_102.bmp differ
diff --git a/dll/win32/imm32/res/1033_Bitmap_103.bmp 
b/dll/win32/imm32/res/1033_Bitmap_103.bmp
new file mode 100644
index 00000000000..44fdf073c2f
Binary files /dev/null and b/dll/win32/imm32/res/1033_Bitmap_103.bmp differ
diff --git a/dll/win32/imm32/res/1033_Bitmap_104.bmp 
b/dll/win32/imm32/res/1033_Bitmap_104.bmp
new file mode 100644
index 00000000000..384b6cae779
Binary files /dev/null and b/dll/win32/imm32/res/1033_Bitmap_104.bmp differ
diff --git a/dll/win32/imm32/res/1033_Bitmap_105.bmp 
b/dll/win32/imm32/res/1033_Bitmap_105.bmp
new file mode 100644
index 00000000000..49dc2b9bc84
Binary files /dev/null and b/dll/win32/imm32/res/1033_Bitmap_105.bmp differ
diff --git a/dll/win32/imm32/res/1033_Bitmap_106.bmp 
b/dll/win32/imm32/res/1033_Bitmap_106.bmp
new file mode 100644
index 00000000000..01890744f87
Binary files /dev/null and b/dll/win32/imm32/res/1033_Bitmap_106.bmp differ
diff --git a/dll/win32/imm32/res/1033_Bitmap_107.bmp 
b/dll/win32/imm32/res/1033_Bitmap_107.bmp
new file mode 100644
index 00000000000..755312a30fc
Binary files /dev/null and b/dll/win32/imm32/res/1033_Bitmap_107.bmp differ
diff --git a/dll/win32/imm32/res/1033_Bitmap_108.bmp 
b/dll/win32/imm32/res/1033_Bitmap_108.bmp
new file mode 100644
index 00000000000..d98a6107674
Binary files /dev/null and b/dll/win32/imm32/res/1033_Bitmap_108.bmp differ
diff --git a/dll/win32/imm32/res/2052_Bitmap_201.bmp 
b/dll/win32/imm32/res/2052_Bitmap_201.bmp
new file mode 100644
index 00000000000..d38078fa5bd
Binary files /dev/null and b/dll/win32/imm32/res/2052_Bitmap_201.bmp differ
diff --git a/dll/win32/imm32/res/2052_Bitmap_202.bmp 
b/dll/win32/imm32/res/2052_Bitmap_202.bmp
new file mode 100644
index 00000000000..ea86471d6f4
Binary files /dev/null and b/dll/win32/imm32/res/2052_Bitmap_202.bmp differ
diff --git a/dll/win32/imm32/res/2052_Bitmap_203.bmp 
b/dll/win32/imm32/res/2052_Bitmap_203.bmp
new file mode 100644
index 00000000000..d2def3fe5a7
Binary files /dev/null and b/dll/win32/imm32/res/2052_Bitmap_203.bmp differ
diff --git a/dll/win32/imm32/res/2052_Bitmap_204.bmp 
b/dll/win32/imm32/res/2052_Bitmap_204.bmp
new file mode 100644
index 00000000000..b40316891af
Binary files /dev/null and b/dll/win32/imm32/res/2052_Bitmap_204.bmp differ
diff --git a/dll/win32/imm32/res/2052_Bitmap_205.bmp 
b/dll/win32/imm32/res/2052_Bitmap_205.bmp
new file mode 100644
index 00000000000..6a3e43bea2e
Binary files /dev/null and b/dll/win32/imm32/res/2052_Bitmap_205.bmp differ
diff --git a/dll/win32/imm32/res/2052_Bitmap_206.bmp 
b/dll/win32/imm32/res/2052_Bitmap_206.bmp
new file mode 100644
index 00000000000..f86ae147591
Binary files /dev/null and b/dll/win32/imm32/res/2052_Bitmap_206.bmp differ
diff --git a/dll/win32/imm32/res/2052_Bitmap_207.bmp 
b/dll/win32/imm32/res/2052_Bitmap_207.bmp
new file mode 100644
index 00000000000..baa4c76d272
Binary files /dev/null and b/dll/win32/imm32/res/2052_Bitmap_207.bmp differ
diff --git a/dll/win32/imm32/res/2052_Bitmap_208.bmp 
b/dll/win32/imm32/res/2052_Bitmap_208.bmp
new file mode 100644
index 00000000000..e29b9c40973
Binary files /dev/null and b/dll/win32/imm32/res/2052_Bitmap_208.bmp differ
diff --git a/dll/win32/imm32/res/2052_Bitmap_209.bmp 
b/dll/win32/imm32/res/2052_Bitmap_209.bmp
new file mode 100644
index 00000000000..485074a9469
Binary files /dev/null and b/dll/win32/imm32/res/2052_Bitmap_209.bmp differ
diff --git a/dll/win32/imm32/resource.h b/dll/win32/imm32/resource.h
new file mode 100644
index 00000000000..9dba6404099
--- /dev/null
+++ b/dll/win32/imm32/resource.h
@@ -0,0 +1,19 @@
+/* Bitmap IDs */
+#define IDB_T1_BACKSPACE    100
+#define IDB_T1_TAB          101
+#define IDB_T1_CAPS         102
+#define IDB_T1_ENTER        103
+#define IDB_T1_SHIFT        104
+#define IDB_T1_CTRL         105
+#define IDB_T1_ESCAPE       106
+#define IDB_T1_ALT          107
+#define IDB_T1_CHARS        108
+#define IDB_C1_BACKSPACE    201
+#define IDB_C1_TAB          202
+#define IDB_C1_CAPS         203
+#define IDB_C1_ENTER        204
+#define IDB_C1_SHIFT        205
+#define IDB_C1_INS          206
+#define IDB_C1_DEL          207
+#define IDB_C1_ESCAPE       208
+#define IDB_C1_CHARS        209
diff --git a/dll/win32/imm32/softkbd.c b/dll/win32/imm32/softkbd.c
index 551331ffe40..8cf17248760 100644
--- a/dll/win32/imm32/softkbd.c
+++ b/dll/win32/imm32/softkbd.c
@@ -1,22 +1,53 @@
 /*
  * PROJECT:     ReactOS IMM32
  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
- * PURPOSE:     Implementing IMM Software Keyboard
+ * PURPOSE:     Implementing IME Soft Keyboard
  * COPYRIGHT:   Copyright 2023 Katayama Hirofumi MZ 
<[email protected]>
  */
 
 #include "precomp.h"
+#include "resource.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(imm);
 
-static UINT s_uScanCode[256];
-static RECT s_rcWorkArea;
-static POINT s_ptRaiseEdge;
-static LOGFONTW s_lfSKT1Font;
-static BOOL s_bWannaInitSoftKBD = TRUE;
+/*
+ * There are two types of IME Soft Keyboard: Type T1 and Type C1.
+ * T1 is created for Traditional Chinese but not limitted to it.
+ * C1 is created for Simplified Chinese but not limitted to it.
+ * Type C1 has SHIFT status while Type T1 hasn't.
+ */
+
+static UINT guScanCode[256]; /* Mapping: virtual key --> scan code */
+static POINT gptRaiseEdge; /* Border + Edge metrics */
+static BOOL g_bWantSoftKBDMetrics = TRUE;
+
+static inline BOOL
+Imm32PtInRect(
+    _In_ const POINT *ppt,
+    _In_ LONG x,
+    _In_ LONG y,
+    _In_ LONG cx,
+    _In_ LONG cy)
+{
+    return (x <= ppt->x) && (ppt->x < x + cx) && (y <= ppt->y) && (ppt->y < y 
+ cy);
+}
+
+static inline INT
+Imm32Clamp(
+    _In_ INT x,
+    _In_ INT xMin,
+    _In_ INT xMax)
+{
+    if (x < xMin)
+        return xMin;
+    if (x > xMax)
+        return xMax;
+    return x;
+}
 
 static VOID
-Imm32GetAllMonitorSize(_Out_ LPRECT prcWork)
+Imm32GetAllMonitorSize(
+    _Out_ LPRECT prcWork)
 {
     if (GetSystemMetrics(SM_CMONITORS) == 1)
     {
@@ -31,8 +62,8 @@ Imm32GetAllMonitorSize(_Out_ LPRECT prcWork)
 }
 
 static BOOL
-Imm32GetNearestMonitorSize(
-    _In_ HWND hwnd,
+Imm32GetNearestWorkArea(
+    _In_opt_ HWND hwnd,
     _Out_ LPRECT prcWork)
 {
     HMONITOR hMonitor;
@@ -46,39 +77,1054 @@ Imm32GetNearestMonitorSize(
 
     hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
     if (!hMonitor)
+    {
+        ERR("hwnd: %p\n", hwnd);
         return FALSE;
+    }
 
     ZeroMemory(&mi, sizeof(mi));
     mi.cbSize = sizeof(mi);
     GetMonitorInfoW(hMonitor, &mi);
-
     *prcWork = mi.rcWork;
     return TRUE;
 }
 
-/* Software keyboard window procedure (Traditional Chinese) */
+/*****************************************************************************
+ * IME Soft Keyboard Type T1
+ */
+
+#define T1_CLASSNAMEW L"SoftKBDClsT1"
+
+#undef DEFINE_T1K
+#define DEFINE_T1K(t1k_code, virtual_key_code, t1k_code_name, 
virtual_key_name, is_special) \
+    t1k_code_name = t1k_code,
+
+/* Define T1 internal codes (T1K_...) */
+typedef enum T1KEY
+{
+#include "t1keys.h"
+} T1KEY;
+
+#undef DEFINE_T1K
+#define DEFINE_T1K(t1k_code, virtual_key_code, t1k_code_name, 
virtual_key_name, is_special) \
+    virtual_key_code,
+
+#define T1K_MAX 60
+
+/* Mapping: T1K --> Virtual Key */
+const BYTE gT1K2VK[T1K_MAX] =
+{
+#include "t1keys.h"
+};
+
+typedef struct T1WINDOW
+{
+    INT cxDefWidth;           /* Regular key width */
+    INT cxWidth47;            /* [BackSpace] width */
+    INT cxWidth48;            /* [Tab] width */
+    INT cxWidth49;            /* [Caps] width */
+    INT cxWidth50;            /* [Enter] width */
+    INT cxWidth51or52;        /* [Shift] width */
+    INT cxWidth53or54;        /* [Ctrl] width */
+    INT cxWidth55or56;        /* [Alt] width */
+    INT cxWidth57;            /* [Esc] width */
+    INT cxWidth58;            /* [Space] width */
+    INT cyDefHeight;          /* Regular key height */
+    INT cyHeight50;           /* [Enter] height */
+    POINT KeyPos[T1K_MAX];    /* T1K --> POINT */
+    WCHAR chKeyChar[48];      /* T1K --> WCHAR */
+    HBITMAP hbmKeyboard;      /* The keyboard image */
+    DWORD CharSet;            /* LOGFONT.lfCharSet */
+    UINT PressedKey;          /* Currently pressed key */
+    POINT pt0, pt1;           /* The soft keyboard window position */
+    LPARAM KeyboardSubType;   /* See 
IMC_GETSOFTKBDSUBTYPE/IMC_SETSOFTKBDSUBTYPE */
+} T1WINDOW, *PT1WINDOW;
+
+#define T1_KEYPOS(iKey) pT1->KeyPos[iKey]
+
+static LOGFONTW g_T1LogFont;
+
+static void
+T1_GetTextMetric(_Out_ LPTEXTMETRICW ptm)
+{
+    WCHAR wch;
+    SIZE textSize;
+    HFONT hFont;
+    HGDIOBJ hFontOld;
+    HDC hDC;
+#ifndef NDEBUG
+    WCHAR szFace[LF_FACESIZE];
+#endif
+
+    ZeroMemory(&g_T1LogFont, sizeof(g_T1LogFont));
+    g_T1LogFont.lfHeight = -12;
+    g_T1LogFont.lfWeight = FW_NORMAL;
+    g_T1LogFont.lfCharSet = CHINESEBIG5_CHARSET;
+#ifdef NO_HACK /* FIXME: We lack proper Asian fonts! */
+    g_T1LogFont.lfOutPrecision = OUT_TT_ONLY_PRECIS;
+    g_T1LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+    g_T1LogFont.lfQuality = PROOF_QUALITY;
+    g_T1LogFont.lfPitchAndFamily = FF_MODERN | FIXED_PITCH;
+#else
+    StringCchCopyW(g_T1LogFont.lfFaceName, _countof(g_T1LogFont.lfFaceName), 
L"MS Shell Dlg");
+#endif
+    hFont = CreateFontIndirectW(&g_T1LogFont);
+
+    hDC = GetDC(NULL);
+    hFontOld = SelectObject(hDC, hFont);
+
+#ifndef NDEBUG
+    GetTextFaceW(hDC, _countof(szFace), szFace);
+    TRACE("szFace: %s\n", debugstr_w(szFace));
+#endif
+
+    GetTextMetricsW(hDC, ptm);
+
+    wch = 0x4E11; /* U+4E11: δΈ‘ */
+    if (GetTextExtentPoint32W(hDC, &wch, 1, &textSize) && textSize.cx > 
ptm->tmMaxCharWidth)
+        ptm->tmMaxCharWidth = textSize.cx;
+
+    DeleteObject(SelectObject(hDC, hFontOld));
+    ReleaseDC(NULL, hDC);
+}
+
+static void
+T1_InitButtonPos(_Out_ PT1WINDOW pT1)
+{
+    TEXTMETRICW tm;
+    LONG cxLarge, cyLarge;
+    LONG xKey1, yKey1, xKey2, yKey2, xKey3, yKey3;
+    LONG yKey4, xKey4, xKey5, yKey5, xKey6, xKey7;
+    INT iKey;
+
+    T1_GetTextMetric(&tm);
+
+    cxLarge = (3 * tm.tmMaxCharWidth + 18) / 2;
+    cyLarge = tm.tmHeight + 8;
+
+    /* key widths and heights */
+    pT1->cxDefWidth = (2 * tm.tmMaxCharWidth + 12) / 2;
+    pT1->cxWidth47 = (2 * tm.tmMaxCharWidth + 12) / 2 + 1;
+    pT1->cxWidth49 = (4 * tm.tmMaxCharWidth + 24) / 2 + 3;
+    pT1->cxWidth51or52 = (5 * tm.tmMaxCharWidth + 30) / 2 + 5;
+    pT1->cxWidth58 = 4 * (3 * tm.tmMaxCharWidth + 18) / 2 + 15;
+    pT1->cxWidth48 = pT1->cxWidth50 = cxLarge + 2;
+    pT1->cxWidth53or54 = pT1->cxWidth55or56 = cxLarge + 2;
+    pT1->cyHeight50 = 2 * (tm.tmHeight + 8) + 3;
+    pT1->cxWidth57 = cxLarge + 1;
+    pT1->cyDefHeight = cyLarge;
+
+    /* First row */
+    xKey1 = gptRaiseEdge.x + 3;
+    yKey1 = gptRaiseEdge.y + 3;
+    for (iKey = 0; iKey < T1K_Q; ++iKey)
+    {
+        T1_KEYPOS(iKey).x = xKey1;
+        T1_KEYPOS(iKey).y = yKey1;
+        xKey1 += pT1->cxDefWidth + 3;
+    }
+    T1_KEYPOS(T1K_BACKSPACE).y = yKey1;
+    T1_KEYPOS(T1K_BACKSPACE).x = xKey1;
+
+    /* 2nd row */
+    xKey2 = 3 + gptRaiseEdge.x + pT1->cxWidth48 + 3;
+    yKey2 = 3 + yKey1 + cyLarge;
+    T1_KEYPOS(T1K_TAB).x = gptRaiseEdge.x + 3;
+    T1_KEYPOS(T1K_TAB).y = yKey2;
+    for (iKey = T1K_Q; iKey < T1K_A; ++iKey)
+    {
+        T1_KEYPOS(iKey).x = xKey2;
+        T1_KEYPOS(iKey).y = yKey2;
+        xKey2 += pT1->cxDefWidth + 3;
+    }
+    T1_KEYPOS(T1K_ENTER).x = xKey2;
+    T1_KEYPOS(T1K_ENTER).y = yKey2;
+
+    /* 3rd row */
+    xKey3 = gptRaiseEdge.x + 3 + pT1->cxWidth49 + 3;
+    yKey3 = yKey2 + cyLarge + 3;
+    T1_KEYPOS(T1K_CAPS).x = gptRaiseEdge.x + 3;
+    T1_KEYPOS(T1K_CAPS).y = yKey3;
+    for (iKey = T1K_A; iKey < T1K_Z; ++iKey)
+    {
+        T1_KEYPOS(iKey).x = xKey3;
+        T1_KEYPOS(iKey).y = yKey3;
+        xKey3 += pT1->cxDefWidth + 3;
+    }
+
+    /* 4th row */
+    xKey4 = gptRaiseEdge.x + pT1->cxWidth51or52 + 3 + 3;
+    yKey4 = yKey3 + cyLarge + 3;
+    T1_KEYPOS(T1K_L_SHIFT).x = gptRaiseEdge.x + 3;
+    T1_KEYPOS(T1K_L_SHIFT).y = yKey4;
+    for (iKey = T1K_Z; iKey < T1K_BACKSPACE; ++iKey)
+    {
+        T1_KEYPOS(iKey).x = xKey4;
+        T1_KEYPOS(iKey).y = yKey4;
+        xKey4 += pT1->cxDefWidth + 3;
+    }
+    T1_KEYPOS(T1K_R_SHIFT).x = xKey4;
+    T1_KEYPOS(T1K_R_SHIFT).y = yKey4;
+
+    /* 5th row */
+    xKey5 = gptRaiseEdge.x + 3 + pT1->cxWidth53or54 + 3;
+    T1_KEYPOS(T1K_L_CTRL).x = gptRaiseEdge.x + 3;
+    T1_KEYPOS(T1K_ESCAPE).x = xKey5;
+    T1_KEYPOS(T1K_L_ALT).x = xKey5 + pT1->cxWidth57 + 3;
+
+    yKey5 = yKey4 + cyLarge + 3;
+    T1_KEYPOS(T1K_L_CTRL).y = T1_KEYPOS(T1K_ESCAPE).y = T1_KEYPOS(T1K_L_ALT).y 
= yKey5;
+    T1_KEYPOS(T1K_R_ALT).y = T1_KEYPOS(T1K_SPACE).y = T1_KEYPOS(T1K_R_CTRL).y 
= yKey5;
+
+    xKey6 = xKey5 + pT1->cxWidth57 + 3 + pT1->cxWidth55or56 + 3;
+    T1_KEYPOS(T1K_SPACE).x = xKey6;
+
+    xKey7 = xKey6 + pT1->cxWidth58 + 3;
+    T1_KEYPOS(T1K_R_ALT).x = xKey7;
+    T1_KEYPOS(T1K_R_CTRL).x = xKey7 + pT1->cxWidth57 + pT1->cxWidth55or56 + 6;
+}
+
+/* Draw keyboard key edge */
+static void
+T1_DrawConvexRect(
+    _In_ HDC hDC,
+    _In_ INT x,
+    _In_ INT y,
+    _In_ INT width,
+    _In_ INT height)
+{
+    HGDIOBJ hBlackPen = GetStockObject(BLACK_PEN);
+    HGDIOBJ hLtGrayBrush = GetStockObject(LTGRAY_BRUSH);
+    HGDIOBJ hGrayBrush = GetStockObject(GRAY_BRUSH);
+    INT dx = width + 4, dy = height + 4;
+    INT x0 = x - 2, y0 = y + height + 2;
+
+    /* Face */
+    SelectObject(hDC, hBlackPen);
+    SelectObject(hDC, hLtGrayBrush);
+    Rectangle(hDC, x0, y - 2, x0 + dx, y0);
+
+    /* Rounded corners */
+    PatBlt(hDC, x0, y - 2, 1, 1, PATCOPY);
+    PatBlt(hDC, x0, y0, 1, -1, PATCOPY);
+    PatBlt(hDC, x0 + dx, y - 2, -1, 1, PATCOPY);
+    PatBlt(hDC, x0 + dx, y0, -1, -1, PATCOPY);
+
+    /* Light edge */
+    PatBlt(hDC, x0 + 1, y + dy - 3, 1, 2 - dy, WHITENESS);
+    PatBlt(hDC, x0 + 1, y - 1, dx - 2, 1, WHITENESS);
+
+    /* Dark edge */
+    SelectObject(hDC, hGrayBrush);
+    PatBlt(hDC, x0 + 1, y + dy - 3, dx - 2, -1, PATCOPY);
+    PatBlt(hDC, x0 + dx - 1, y + dy - 3, -1, 2 - dy, PATCOPY);
+}
+
+static void
+T1_DrawBitmap(
+    _In_ HDC hDC,
+    _In_ INT x,
+    _In_ INT y,
+    _In_ INT cx,
+    _In_ INT cy,
+    _In_ INT nBitmapID)
+{
+    HBITMAP hBitmap = LoadBitmapW(ghImm32Inst, MAKEINTRESOURCEW(nBitmapID));
+    HDC hMemDC = CreateCompatibleDC(hDC);
+    HGDIOBJ hbmOld = SelectObject(hMemDC, hBitmap);
+    BitBlt(hDC, x, y, cx, cy, hMemDC, 0, 0, SRCCOPY);
+    SelectObject(hMemDC, hbmOld);
+    DeleteObject(hBitmap);
+    DeleteDC(hMemDC);
+}
+
+static void
+T1_DrawLabels(
+    _In_ HDC hDC,
+    _In_ const T1WINDOW *pT1,
+    _In_ LPCWSTR pszBmpName)
+{
+    HBITMAP hBitmap = LoadBitmapW(ghImm32Inst, pszBmpName);
+    HDC hdcMem = CreateCompatibleDC(hDC);
+    HGDIOBJ hbmOld = SelectObject(hdcMem, hBitmap);
+    INT iKey;
+    for (iKey = 0; iKey < T1K_BACKSPACE; ++iKey)
+    {
+        const POINT *ppt = &T1_KEYPOS(iKey);
+        BitBlt(hDC, ppt->x, ppt->y, 8, 8, hdcMem, iKey * 8, 0, SRCCOPY);
+    }
+    SelectObject(hdcMem, hbmOld);
+    DeleteDC(hdcMem);
+    DeleteObject(hBitmap);
+}
+
+static void
+T1_InitBitmap(
+    _In_ HWND hWnd,
+    _Inout_ PT1WINDOW pT1)
+{
+    HDC hDC, hMemDC;
+    HGDIOBJ hNullPen = GetStockObject(NULL_PEN), hbrLtGray = 
GetStockObject(LTGRAY_BRUSH);
+    RECT rc;
+    INT iKey;
+
+    /* Create the bitmap */
+    hDC = GetDC(hWnd);
+    hMemDC = CreateCompatibleDC(hDC);
+    GetClientRect(hWnd, &rc);
+    pT1->hbmKeyboard = CreateCompatibleBitmap(hDC, rc.right - rc.left, 
rc.bottom - rc.top);
+    ReleaseDC(hWnd, hDC);
+
+    /* Draw keyboard face */
+    SelectObject(hMemDC, pT1->hbmKeyboard);
+    SelectObject(hMemDC, hNullPen);
+    SelectObject(hMemDC, hbrLtGray);
+    Rectangle(hMemDC, rc.left, rc.top, rc.right + 1, rc.bottom + 1);
+    DrawEdge(hMemDC, &rc, EDGE_RAISED, BF_RECT);
+
+    /* 53 --> Left [Ctrl] */
+    T1_DrawConvexRect(hMemDC,
+                      T1_KEYPOS(T1K_L_CTRL).x, T1_KEYPOS(T1K_L_CTRL).y,
+                      pT1->cxWidth53or54, pT1->cyDefHeight);
+    T1_DrawBitmap(hMemDC,
+                  pT1->cxWidth53or54 / 2 + T1_KEYPOS(T1K_L_CTRL).x - 8,
+                  pT1->cyDefHeight   / 2 + T1_KEYPOS(T1K_L_CTRL).y - 4,
+                  16, 9, IDB_T1_CTRL);
+
+    /* 54 --> Right [Ctrl] */
+    T1_DrawConvexRect(hMemDC,
+                      T1_KEYPOS(T1K_R_CTRL).x, T1_KEYPOS(T1K_R_CTRL).y,
+                      pT1->cxWidth53or54, pT1->cyDefHeight);
+    T1_DrawBitmap(hMemDC,
+                  pT1->cxWidth53or54 / 2 + T1_KEYPOS(T1K_R_CTRL).x - 8,
+                  pT1->cyDefHeight   / 2 + T1_KEYPOS(T1K_R_CTRL).y - 4,
+                  16, 9, IDB_T1_CTRL);
+
+    /* 57 --> [Esc] */
+    T1_DrawConvexRect(hMemDC,
+                      T1_KEYPOS(T1K_ESCAPE).x, T1_KEYPOS(T1K_ESCAPE).y,
+                      pT1->cxWidth57, pT1->cyDefHeight);
+    T1_DrawBitmap(hMemDC,
+                  pT1->cxWidth57   / 2 + T1_KEYPOS(T1K_ESCAPE).x - 9,
+                  pT1->cyDefHeight / 2 + T1_KEYPOS(T1K_ESCAPE).y - 4,
+                  18, 9, IDB_T1_ESCAPE);
+
+    /* 55 --> Left [Alt] */
+    T1_DrawConvexRect(hMemDC,
+                      T1_KEYPOS(T1K_L_ALT).x, T1_KEYPOS(T1K_L_ALT).y,
+                      pT1->cxWidth55or56, pT1->cyDefHeight);
+    T1_DrawBitmap(hMemDC,
+                  pT1->cxWidth55or56 / 2 + T1_KEYPOS(T1K_L_ALT).x - 8,
+                  pT1->cyDefHeight   / 2 + T1_KEYPOS(T1K_L_ALT).y - 4,
+                  16, 9, IDB_T1_ALT);
+
+    /* 56 --> Right [Alt] */
+    T1_DrawConvexRect(hMemDC,
+                      T1_KEYPOS(T1K_R_ALT).x, T1_KEYPOS(T1K_R_ALT).y,
+                      pT1->cxWidth55or56, pT1->cyDefHeight);
+    T1_DrawBitmap(hMemDC,
+                  pT1->cxWidth55or56 / 2 + T1_KEYPOS(T1K_R_ALT).x - 8,
+                  pT1->cyDefHeight   / 2 + T1_KEYPOS(T1K_R_ALT).y - 4,
+                  16, 9, IDB_T1_ALT);
+
+    /* 58 --> [Space] */
+    T1_DrawConvexRect(hMemDC,
+                      T1_KEYPOS(T1K_SPACE).x, T1_KEYPOS(T1K_SPACE).y,
+                      pT1->cxWidth58, pT1->cyDefHeight);
+
+    /* 51 --> Left [Shift] */
+    T1_DrawConvexRect(hMemDC,
+                      T1_KEYPOS(T1K_L_SHIFT).x, T1_KEYPOS(T1K_L_SHIFT).y,
+                      pT1->cxWidth51or52, pT1->cyDefHeight);
+    T1_DrawBitmap(hMemDC,
+                  pT1->cxWidth51or52 / 2 + T1_KEYPOS(T1K_L_SHIFT).x - 11,
+                  pT1->cyDefHeight   / 2 + T1_KEYPOS(T1K_L_SHIFT).y - 4,
+                  23, 9, IDB_T1_SHIFT);
+
+    /* 52 --> Right [Shift] */
+    T1_DrawConvexRect(hMemDC,
+                      T1_KEYPOS(T1K_R_SHIFT).x, T1_KEYPOS(T1K_R_SHIFT).y,
+                      pT1->cxWidth51or52, pT1->cyDefHeight);
+    T1_DrawBitmap(hMemDC,
+                  pT1->cxWidth51or52 / 2 + T1_KEYPOS(T1K_R_SHIFT).x - 11,
+                  pT1->cyDefHeight   / 2 + T1_KEYPOS(T1K_R_SHIFT).y - 4,
+                  23, 9, IDB_T1_SHIFT);
+
+    /* 49 --> [Caps] */
+    T1_DrawConvexRect(hMemDC,
+                      T1_KEYPOS(T1K_CAPS).x, T1_KEYPOS(T1K_CAPS).y,
+                      pT1->cxWidth49, pT1->cyDefHeight);
+    T1_DrawBitmap(hMemDC,
+                  pT1->cxWidth49   / 2 + T1_KEYPOS(T1K_CAPS).x - 11,
+                  pT1->cyDefHeight / 2 + T1_KEYPOS(T1K_CAPS).y - 4,
+                  22, 9, IDB_T1_CAPS);
+
+    /* 48 --> [Tab] */
+    T1_DrawConvexRect(hMemDC,
+                      T1_KEYPOS(T1K_TAB).x, T1_KEYPOS(T1K_TAB).y,
+                      pT1->cxWidth48, pT1->cyDefHeight);
+    T1_DrawBitmap(hMemDC,
+                  pT1->cxWidth48   / 2 + T1_KEYPOS(T1K_TAB).x - 8,
+                  pT1->cyDefHeight / 2 + T1_KEYPOS(T1K_TAB).y - 4,
+                  16, 9, IDB_T1_TAB);
+
+    /* 50 --> [Enter] */
+    T1_DrawConvexRect(hMemDC,
+                      T1_KEYPOS(T1K_ENTER).x, T1_KEYPOS(T1K_ENTER).y,
+                      pT1->cxWidth50, pT1->cyHeight50);
+    T1_DrawBitmap(hMemDC,
+                  pT1->cxWidth50  / 2 + T1_KEYPOS(T1K_ENTER).x - 13,
+                  pT1->cyHeight50 / 2 + T1_KEYPOS(T1K_ENTER).y - 4,
+                  26, 9, IDB_T1_ENTER);
+
+    /* 47 --> [BackSpace] */
+    T1_DrawConvexRect(hMemDC,
+                      T1_KEYPOS(T1K_BACKSPACE).x, T1_KEYPOS(T1K_BACKSPACE).y,
+                      pT1->cxWidth47, pT1->cyDefHeight);
+    T1_DrawBitmap(hMemDC,
+                  pT1->cxWidth47   / 2 + T1_KEYPOS(T1K_BACKSPACE).x - 8,
+                  pT1->cyDefHeight / 2 + T1_KEYPOS(T1K_BACKSPACE).y - 4,
+                  16, 9, IDB_T1_BACKSPACE);
+
+    /* Regular keys */
+    for (iKey = 0; iKey < T1K_BACKSPACE; ++iKey)
+    {
+        LPPOINT ppt = &T1_KEYPOS(iKey);
+        T1_DrawConvexRect(hMemDC, ppt->x, ppt->y, pT1->cxDefWidth, 
pT1->cyDefHeight);
+    }
+
+    T1_DrawLabels(hMemDC, pT1, MAKEINTRESOURCEW(IDB_T1_CHARS));
+    DeleteDC(hMemDC);
+}
+
+static INT
+T1_OnCreate(
+    _In_ HWND hWnd)
+{
+    PT1WINDOW pT1;
+    HGLOBAL hGlobal = GlobalAlloc(GHND, sizeof(T1WINDOW));
+    if (!hGlobal)
+        return -1;
+
+    pT1 = (PT1WINDOW)GlobalLock(hGlobal);
+    if (!pT1)
+    {
+        GlobalFree(hGlobal);
+        return -1;
+    }
+
+    SetWindowLongPtrW(hWnd, 0, (LONG_PTR)hGlobal);
+    pT1->pt1.x = pT1->pt1.y = -1;
+    pT1->PressedKey = T1K_NONE;
+    pT1->CharSet = CHINESEBIG5_CHARSET;
+
+    T1_InitButtonPos(pT1);
+    T1_InitBitmap(hWnd, pT1);
+    GlobalUnlock(hGlobal);
+
+    return 0;
+}
+
+static void
+T1_DrawDragBorder(
+    _In_ HWND hWnd,
+    _In_ const POINT *ppt1,
+    _In_ const POINT *ppt2)
+{
+    INT cxBorder = GetSystemMetrics(SM_CXBORDER), cyBorder = 
GetSystemMetrics(SM_CYBORDER);
+    INT x = ppt1->x - ppt2->x, y = ppt1->y - ppt2->y;
+    HGDIOBJ hGrayBrush = GetStockObject(GRAY_BRUSH);
+    RECT rc;
+    HDC hDisplayDC;
+
+    GetWindowRect(hWnd, &rc);
+    hDisplayDC = CreateDCW(L"DISPLAY", NULL, NULL, NULL);
+    SelectObject(hDisplayDC, hGrayBrush);
+    PatBlt(hDisplayDC, x, y, rc.right - rc.left - cxBorder, cyBorder, 
PATINVERT);
+    PatBlt(hDisplayDC, x, cyBorder + y, cxBorder, rc.bottom - rc.top - 
cyBorder, PATINVERT);
+    PatBlt(hDisplayDC, x + cxBorder, y + rc.bottom - rc.top, rc.right - 
rc.left - cxBorder, -cyBorder, PATINVERT);
+    PatBlt(hDisplayDC, x + rc.right - rc.left, y, -cxBorder, rc.bottom - 
rc.top - cyBorder, PATINVERT);
+    DeleteDC(hDisplayDC);
+}
+
+static void
+T1_OnDestroy(
+    _In_ HWND hWnd)
+{
+    HGLOBAL hGlobal;
+    PT1WINDOW pT1;
+    HWND hwndOwner;
+
+    hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
+    pT1 = (PT1WINDOW)GlobalLock(hGlobal);
+    if (!hGlobal || !pT1)
+        return;
+
+    if (pT1->pt1.x != -1 && pT1->pt1.y != -1)
+        T1_DrawDragBorder(hWnd, &pT1->pt0, &pT1->pt1);
+
+    DeleteObject(pT1->hbmKeyboard);
+    GlobalUnlock(hGlobal);
+    GlobalFree(hGlobal);
+
+    hwndOwner = GetWindow(hWnd, GW_OWNER);
+    if (hwndOwner)
+        SendMessageW(hwndOwner, WM_IME_NOTIFY, IMN_SOFTKBDDESTROYED, 0);
+}
+
+static void
+T1_InvertButton(
+    _In_ HWND hWnd,
+    _In_ HDC hDC,
+    _In_ const T1WINDOW *pT1,
+    _In_ UINT iPressed)
+{
+    INT cxWidth = pT1->cxDefWidth, cyHeight = pT1->cyDefHeight;
+    HDC hChoiceDC;
+
+    if (iPressed >= T1K_NONE)
+        return;
+
+    if (hDC)
+        hChoiceDC = hDC;
+    else
+        hChoiceDC = GetDC(hWnd);
+
+    if (iPressed >= T1K_BACKSPACE)
+    {
+        switch (iPressed)
+        {
+            case T1K_BACKSPACE:
+                cxWidth = pT1->cxWidth47;
+                break;
+            case T1K_TAB:
+                cxWidth = pT1->cxWidth48;
+                break;
+            case T1K_ENTER:
+                pT1 = pT1;
+                cxWidth = pT1->cxWidth50;
+                cyHeight = pT1->cyHeight50;
+                break;
+            case T1K_ESCAPE:
+                cxWidth = pT1->cxWidth57;
+                break;
+            case T1K_SPACE:
+                cxWidth = pT1->cxWidth58;
+                break;
+            default:
+                cxWidth = 0;
+                MessageBeep(0xFFFFFFFF);
+                break;
+        }
+    }
+
+    if (cxWidth > 0)
+    {
+        PatBlt(hChoiceDC,
+               T1_KEYPOS(iPressed).x - 1, T1_KEYPOS(iPressed).y - 1,
+               cxWidth + 2, cyHeight + 2,
+               DSTINVERT);
+    }
+
+    if (!hDC)
+        ReleaseDC(hWnd, hChoiceDC);
+}
+
+static void
+T1_OnDraw(
+    _In_ HDC hDC,
+    _In_ HWND hWnd)
+{
+    HGLOBAL hGlobal;
+    PT1WINDOW pT1;
+    HDC hMemDC;
+    RECT rc;
+
+    hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
+    pT1 = (PT1WINDOW)GlobalLock(hGlobal);
+    if (!hGlobal || !pT1)
+        return;
+
+    hMemDC = CreateCompatibleDC(hDC);
+    SelectObject(hMemDC, pT1->hbmKeyboard);
+    GetClientRect(hWnd, &rc);
+    BitBlt(hDC, 0, 0, rc.right - rc.left, rc.bottom - rc.top, hMemDC, 0, 0, 
SRCCOPY);
+    DeleteDC(hMemDC);
+
+    if (pT1->PressedKey < T1K_NONE)
+        T1_InvertButton(hWnd, hDC, pT1, pT1->PressedKey);
+
+    GlobalUnlock(hGlobal);
+}
+
+static UINT
+T1_HitTest(
+    _In_ const T1WINDOW *pT1,
+    _In_ const POINT *ppt)
+{
+    INT iKey;
+    for (iKey = 0; iKey < T1K_BACKSPACE; ++iKey)
+    {
+        const POINT *pptKey = &T1_KEYPOS(iKey);
+        if (Imm32PtInRect(ppt, pptKey->x, pptKey->y, pT1->cxDefWidth, 
pT1->cyDefHeight))
+            return iKey;
+    }
+
+    if (Imm32PtInRect(ppt, T1_KEYPOS(T1K_BACKSPACE).x, 
T1_KEYPOS(T1K_BACKSPACE).y, pT1->cxWidth47, pT1->cyDefHeight))
+        return T1K_BACKSPACE;
+
+    if (Imm32PtInRect(ppt, T1_KEYPOS(T1K_TAB).x, T1_KEYPOS(T1K_TAB).y, 
pT1->cxWidth48, pT1->cyDefHeight))
+        return T1K_TAB;
+
+    if (Imm32PtInRect(ppt, T1_KEYPOS(T1K_CAPS).x, T1_KEYPOS(T1K_CAPS).y, 
pT1->cxWidth49, pT1->cyDefHeight))
+        return T1K_CAPS;
+
+    if (Imm32PtInRect(ppt, T1_KEYPOS(T1K_ENTER).x, T1_KEYPOS(T1K_ENTER).y, 
pT1->cxWidth50, pT1->cyHeight50))
+        return T1K_ENTER;
+
+    if (Imm32PtInRect(ppt, T1_KEYPOS(T1K_L_SHIFT).x, T1_KEYPOS(T1K_L_SHIFT).y, 
pT1->cxWidth51or52, pT1->cyDefHeight) ||
+        Imm32PtInRect(ppt, T1_KEYPOS(T1K_R_SHIFT).x, T1_KEYPOS(T1K_R_SHIFT).y, 
pT1->cxWidth51or52, pT1->cyDefHeight))
+    {
+        return T1K_L_SHIFT;
+    }
+
+    if (Imm32PtInRect(ppt, T1_KEYPOS(T1K_L_CTRL).x, T1_KEYPOS(T1K_L_CTRL).y, 
pT1->cxWidth53or54, pT1->cyDefHeight) ||
+        Imm32PtInRect(ppt, T1_KEYPOS(T1K_R_CTRL).x, T1_KEYPOS(T1K_R_CTRL).y, 
pT1->cxWidth53or54, pT1->cyDefHeight))
+    {
+        return T1K_L_CTRL;
+    }
+
+    if (Imm32PtInRect(ppt, T1_KEYPOS(T1K_L_ALT).x, T1_KEYPOS(T1K_L_ALT).y, 
pT1->cxWidth55or56, pT1->cyDefHeight) ||
+        Imm32PtInRect(ppt, T1_KEYPOS(T1K_R_ALT).x, T1_KEYPOS(T1K_R_ALT).y, 
pT1->cxWidth55or56, pT1->cyDefHeight))
+    {
+        return T1K_L_ALT;
+    }
+
+    if (Imm32PtInRect(ppt, T1_KEYPOS(T1K_ESCAPE).x, T1_KEYPOS(T1K_ESCAPE).y, 
pT1->cxWidth57, pT1->cyDefHeight))
+        return T1K_ESCAPE;
+
+    if (Imm32PtInRect(ppt, T1_KEYPOS(T1K_SPACE).x, T1_KEYPOS(T1K_SPACE).y, 
pT1->cxWidth58, pT1->cyDefHeight))
+        return T1K_SPACE;
+
+    return T1K_NONE;
+}
+
+static BOOL
+T1_IsValidButton(
+    _In_ UINT iKey,
+    _In_ const T1WINDOW *pT1)
+{
+    if (iKey < T1K_BACKSPACE)
+        return !!pT1->chKeyChar[iKey];
+    return iKey <= T1K_TAB || iKey == T1K_ENTER || (T1K_ESCAPE <= iKey && iKey 
<= T1K_SPACE);
+}
+
+/**
+ * NOTE: The window that has WS_DISABLED style doesn't receive some mouse 
messages.
+ * Use WM_SETCURSOR handling to detect mouse events.
+ */
+static BOOL
+T1_OnSetCursor(
+    _In_ HWND hWnd,
+    _In_ LPARAM lParam)
+{
+    HGLOBAL hGlobal;
+    PT1WINDOW pT1;
+    HCURSOR hCursor;
+    UINT iPressed, iKey;
+    RECT rc, rcWork;
+
+    hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
+    pT1 = (PT1WINDOW)GlobalLock(hGlobal);
+    if (!hGlobal || !pT1)
+        return FALSE;
+
+    if (pT1->pt1.x != -1 && pT1->pt1.y != -1)
+    {
+        SetCursor(LoadCursorW(NULL, (LPCWSTR)IDC_SIZEALL));
+        GlobalUnlock(hGlobal);
+        return TRUE;
+    }
+
+    GetCursorPos(&pT1->pt0);
+    ScreenToClient(hWnd, &pT1->pt0);
+
+    iKey = T1_HitTest(pT1, &pT1->pt0);
+    if (iKey >= T1K_NONE)
+        hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_SIZEALL);
+    else
+        hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_HAND);
+    SetCursor(hCursor);
+
+    if (HIWORD(lParam) == WM_LBUTTONDOWN)
+    {
+        SetCapture(hWnd);
+
+        iPressed = pT1->PressedKey;
+        if (iPressed < T1K_NONE)
+        {
+            UINT iVK = gT1K2VK[iPressed];
+            keybd_event(iVK, guScanCode[iVK], KEYEVENTF_KEYUP, 0);
+            T1_InvertButton(hWnd, NULL, pT1, pT1->PressedKey);
+            pT1->PressedKey = T1K_NONE;
+        }
+
+        if (iKey >= T1K_NONE)
+        {
+            Imm32GetAllMonitorSize(&rcWork);
+            GetCursorPos(&pT1->pt0);
+            GetWindowRect(hWnd, &rc);
+            pT1->pt1.x = pT1->pt0.x - rc.left;
+            pT1->pt1.y = pT1->pt0.y - rc.top;
+            T1_DrawDragBorder(hWnd, &pT1->pt0, &pT1->pt1);
+        }
+        else if (T1_IsValidButton(iKey, pT1))
+        {
+            UINT iVK = gT1K2VK[iKey];
+            keybd_event(iVK, guScanCode[iVK], 0, 0);
+            pT1->PressedKey = iKey;
+            T1_InvertButton(hWnd, 0, pT1, iKey);
+        }
+        else
+        {
+            MessageBeep(0xFFFFFFFF);
+        }
+    }
+
+    return TRUE;
+}
+
+static BOOL
+T1_OnMouseMove(
+    _In_ HWND hWnd)
+{
+    BOOL ret = FALSE;
+    HGLOBAL hGlobal;
+    PT1WINDOW pT1;
+
+    hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
+    pT1 = (PT1WINDOW)GlobalLock(hGlobal);
+    if (!hGlobal || !pT1)
+        return FALSE;
+
+    if (pT1->pt1.x != -1 && pT1->pt1.y != -1)
+    {
+        T1_DrawDragBorder(hWnd, &pT1->pt0, &pT1->pt1);
+        GetCursorPos(&pT1->pt0);
+        T1_DrawDragBorder(hWnd, &pT1->pt0, &pT1->pt1);
+        ret = TRUE;
+    }
+
+    GlobalUnlock(hGlobal);
+    return ret;
+}
+
+static BOOL
+T1_OnButtonUp(
+    _In_ HWND hWnd)
+{
+    BOOL ret = FALSE;
+    HGLOBAL hGlobal;
+    PT1WINDOW pT1;
+    INT x, y, iPressed;
+    HWND hwndOwner, hwndCapture = GetCapture();
+    HIMC hIMC;
+    LPINPUTCONTEXT pIC;
+
+    if (hwndCapture == hWnd)
+        ReleaseCapture();
+
+    hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
+    pT1 = (PT1WINDOW)GlobalLock(hGlobal);
+    if (!hGlobal || !pT1)
+        return FALSE;
+
+    iPressed = pT1->PressedKey;
+    if (iPressed >= T1K_NONE)
+    {
+        if (pT1->pt1.x != -1 && pT1->pt1.y != -1 )
+        {
+            T1_DrawDragBorder(hWnd, &pT1->pt0, &pT1->pt1);
+            x = pT1->pt0.x - pT1->pt1.x;
+            y = pT1->pt0.y - pT1->pt1.y;
+            SetWindowPos(hWnd, NULL, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER 
| SWP_NOSIZE);
+            pT1->pt1.x = pT1->pt1.y = -1;
+            pT1->PressedKey = T1K_NONE;
+            ret = TRUE;
+
+            hwndOwner = GetWindow(hWnd, GW_OWNER);
+            hIMC = (HIMC)GetWindowLongPtrW(hwndOwner, 0);
+            if (hIMC)
+            {
+                pIC = ImmLockIMC(hIMC);
+                if (pIC)
+                {
+                    pIC->fdwInit |= INIT_SOFTKBDPOS;
+                    pIC->ptSoftKbdPos.x = x;
+                    pIC->ptSoftKbdPos.y = y;
+                    ImmUnlockIMC(hIMC);
+                }
+            }
+        }
+    }
+    else
+    {
+        UINT iVK = gT1K2VK[iPressed];
+        keybd_event(iVK, guScanCode[iVK], KEYEVENTF_KEYUP, 0);
+
+        T1_InvertButton(hWnd, 0, pT1, pT1->PressedKey);
+        pT1->PressedKey = T1K_NONE;
+        ret = TRUE;
+    }
+
+    GlobalUnlock(hGlobal);
+    return ret;
+}
+
+static LRESULT
+T1_SetData(
+    _In_ HWND hWnd,
+    _In_ const SOFTKBDDATA *pData)
+{
+    HGLOBAL hGlobal;
+    PT1WINDOW pT1;
+    HDC hDC, hMemDC;
+    HFONT hFont;
+    HGDIOBJ hFontOld, hbmOld;
+    RECT rc;
+    INT iKey;
+
+    hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
+    pT1 = (PT1WINDOW)GlobalLock(hGlobal);
+    if (!hGlobal || !pT1)
+        return 1;
+
+    hDC = GetDC(hWnd);
+    hMemDC = CreateCompatibleDC(hDC);
+    ReleaseDC(hWnd, hDC);
+
+    hbmOld = SelectObject(hMemDC, pT1->hbmKeyboard);
+#if 0 /* The default text color is black */
+    SetTextColor(hMemDC, RGB(0, 0, 0));
+#endif
+    SetBkColor(hMemDC, RGB(192, 192, 192));
+
+    if (pT1->CharSet == DEFAULT_CHARSET)
+    {
+        hFont = CreateFontIndirectW(&g_T1LogFont);
+    }
+    else
+    {
+        LOGFONTW lf = g_T1LogFont;
+        lf.lfCharSet = (BYTE)pT1->CharSet;
+        hFont = CreateFontIndirectW(&lf);
+    }
+    hFontOld = SelectObject(hMemDC, hFont);
+
+    for (iKey = 0; iKey < T1K_BACKSPACE; ++iKey)
+    {
+        INT x0 = T1_KEYPOS(iKey).x, y0 = T1_KEYPOS(iKey).y;
+        INT x = x0 + 6, y = y0 + 8;
+        WCHAR wch = pT1->chKeyChar[iKey] = pData->wCode[0][gT1K2VK[iKey]];
+        SetRect(&rc, x, y, x0 + pT1->cxDefWidth, y0 + pT1->cyDefHeight);
+        ExtTextOutW(hMemDC, x, y, ETO_OPAQUE, &rc, &wch, wch != 0, NULL);
+    }
+
+    DeleteObject(SelectObject(hMemDC, hFontOld));
+    SelectObject(hMemDC, hbmOld);
+    DeleteDC(hMemDC);
+    GlobalUnlock(hGlobal);
+    return 0;
+}
+
+static LRESULT
+T1_OnImeControl(
+    _In_ HWND hWnd,
+    _Inout_ WPARAM wParam,
+    _Inout_ LPARAM lParam)
+{
+    LRESULT ret = 1;
+    PT1WINDOW pT1;
+    HGLOBAL hGlobal;
+
+    switch (wParam)
+    {
+        case IMC_GETSOFTKBDFONT:
+        {
+            TRACE("IMC_GETSOFTKBDFONT: %p\n", lParam);
+            hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
+            pT1 = (PT1WINDOW)GlobalLock(hGlobal);
+            if (hGlobal && pT1)
+            {
+                LPLOGFONTW plf = (LPLOGFONTW)lParam;
+                DWORD CharSet = pT1->CharSet;
+                GlobalUnlock(hGlobal);
+
+                *plf = g_T1LogFont;
+                if (CharSet != DEFAULT_CHARSET)
+                    plf->lfCharSet = (BYTE)CharSet;
+
+                ret = 0;
+            }
+            break;
+        }
+        case IMC_SETSOFTKBDFONT:
+        {
+            const LOGFONTW *plf = (LPLOGFONTW)lParam;
+            TRACE("IMC_SETSOFTKBDFONT: %p\n", lParam);
+            if (g_T1LogFont.lfCharSet == plf->lfCharSet)
+                return 0;
+
+            hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
+            pT1 = (PT1WINDOW)GlobalLock(hGlobal);
+            if (hGlobal && pT1)
+            {
+                pT1->CharSet = plf->lfCharSet;
+                GlobalUnlock(hGlobal);
+                return 0;
+            }
+
+            break;
+        }
+        case IMC_GETSOFTKBDPOS:
+        {
+            RECT rc;
+            TRACE("IMC_GETSOFTKBDPOS\n");
+            GetWindowRect(hWnd, &rc);
+            return MAKELRESULT(rc.left, rc.top);
+        }
+        case IMC_SETSOFTKBDPOS:
+        {
+            POINT pt;
+            HWND hwndParent;
+
+            POINTSTOPOINT(pt, lParam);
+            TRACE("IMC_SETSOFTKBDPOS(%ld, %ld)\n", pt.x, pt.y);
+
+            SetWindowPos(hWnd, NULL, pt.x, pt.y, 0, 0, SWP_NOACTIVATE | 
SWP_NOZORDER | SWP_NOSIZE);
+
+            hwndParent = GetParent(hWnd);
+            if (hwndParent)
+            {
+                HIMC hIMC = (HIMC)GetWindowLongPtrW(hwndParent, 0);
+                if (hIMC)
+                {
+                    LPINPUTCONTEXT pIC = ImmLockIMC(hIMC);
+                    if (pIC)
+                    {
+                        pIC->ptSoftKbdPos.x = pt.x;
+                        pIC->ptSoftKbdPos.y = pt.y;
+                        ImmUnlockIMC(hIMC);
+                        return 0;
+                    }
+                }
+            }
+            break;
+        }
+        case IMC_GETSOFTKBDSUBTYPE:
+        case IMC_SETSOFTKBDSUBTYPE:
+        {
+            TRACE("IMC_GETSOFTKBDSUBTYPE/IMC_SETSOFTKBDSUBTYPE\n");
+            hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
+            pT1 = (PT1WINDOW)GlobalLock(hGlobal);
+            if (!hGlobal || !pT1)
+                return -1;
+
+            ret = pT1->KeyboardSubType;
+
+            if (wParam == IMC_SETSOFTKBDSUBTYPE)
+                pT1->KeyboardSubType = lParam;
+
+            GlobalUnlock(hGlobal);
+            break;
+        }
+        case IMC_SETSOFTKBDDATA:
+        {
+            TRACE("IMC_SETSOFTKBDDATA: %p\n", lParam);
+            ret = T1_SetData(hWnd, (SOFTKBDDATA*)lParam);
+            if (!ret)
+            {
+                InvalidateRect(hWnd, NULL, FALSE);
+                PostMessageW(hWnd, WM_PAINT, 0, 0);
+            }
+            break;
+        }
+    }
+
+    return ret;
+}
+
 static LRESULT CALLBACK
-SKWndProcT1(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+T1_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
     switch (uMsg)
     {
         case WM_CREATE:
         {
-            FIXME("stub\n");
-            return -1;
+            return T1_OnCreate(hWnd);
+        }
+        case WM_DESTROY:
+        {
+            T1_OnDestroy(hWnd);
+            break;
+        }
+        case WM_SETCURSOR:
+        {
+            if (T1_OnSetCursor(hWnd, lParam))
+                break;
+            return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+        }
+        case WM_MOUSEMOVE:
+        {
+            if (T1_OnMouseMove(hWnd))
+                break;
+            return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+        }
+        case WM_PAINT:
+        {
+            PAINTSTRUCT ps;
+            HDC hDC = BeginPaint(hWnd, &ps);
+            T1_OnDraw(hDC, hWnd);
+            EndPaint(hWnd, &ps);
+            break;
+        }
+        case WM_SHOWWINDOW:
+        {
+            if (!lParam && wParam != SW_SHOWNORMAL)
+                T1_OnButtonUp(hWnd);
+            return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+        }
+        case WM_MOUSEACTIVATE:
+        {
+            return MA_NOACTIVATE;
+        }
+        case WM_LBUTTONUP:
+        {
+            if (T1_OnButtonUp(hWnd))
+                break;
+            return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+        }
+        case WM_IME_CONTROL:
+        {
+            return T1_OnImeControl(hWnd, wParam, lParam);
         }
-
         default:
         {
-            return DefWindowProcW(hwnd, uMsg, wParam, lParam);
+            return DefWindowProcW(hWnd, uMsg, wParam, lParam);
         }
     }
+
     return 0;
 }
 
-/* Software keyboard window procedure (Simplified Chinese) */
+/*****************************************************************************
+ * IME Soft Keyboard Type C1
+ */
+
+#define C1_CLASSNAMEW L"SoftKBDClsC1"
+
 static LRESULT CALLBACK
-SKWndProcC1(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+C1_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
     switch (uMsg)
     {
@@ -90,102 +1136,62 @@ SKWndProcC1(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM 
lParam)
 
         default:
         {
-            return DefWindowProcW(hwnd, uMsg, wParam, lParam);
+            return DefWindowProcW(hWnd, uMsg, wParam, lParam);
         }
     }
     return 0;
 }
 
+/*****************************************************************************/
+
 static BOOL
-Imm32RegisterSoftKeyboard(_In_ UINT uType)
+Imm32RegisterSoftKeyboard(
+    _In_ UINT uType)
 {
-    LPCWSTR pszClass;
     WNDCLASSEXW wcx;
-
-    if (uType == 1)
-        pszClass = L"SoftKBDClsT1";
-    else if (uType == 2)
-        pszClass = L"SoftKBDClsC1";
-    else
-        return FALSE;
-
+    LPCWSTR pszClass = ((uType == SOFTKEYBOARD_TYPE_T1) ? T1_CLASSNAMEW : 
C1_CLASSNAMEW);
     if (GetClassInfoExW(ghImm32Inst, pszClass, &wcx))
         return TRUE;
 
     ZeroMemory(&wcx, sizeof(wcx));
-    wcx.cbSize = sizeof(wcx);
-    wcx.style = CS_IME;
-    wcx.cbWndExtra = sizeof(LONG_PTR);
-    wcx.hIcon = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
-    wcx.hInstance = ghImm32Inst;
-    wcx.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_SIZEALL);
+    wcx.cbSize        = sizeof(wcx);
+    wcx.style         = CS_IME;
+    wcx.cbWndExtra    = sizeof(PT1WINDOW);
+    wcx.hIcon         = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
+    wcx.hInstance     = ghImm32Inst;
+    wcx.hCursor       = LoadCursorW(NULL, (LPCWSTR)IDC_SIZEALL);
     wcx.lpszClassName = pszClass;
 
-    if (uType == 1)
+    if (uType == SOFTKEYBOARD_TYPE_T1)
     {
-        wcx.lpfnWndProc = SKWndProcT1;
+        wcx.lpfnWndProc = T1_WindowProc;
         wcx.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
     }
     else
     {
-        wcx.lpfnWndProc = SKWndProcC1;
+        wcx.lpfnWndProc = C1_WindowProc;
         wcx.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH);
     }
 
     return !!RegisterClassExW(&wcx);
 }
 
-static VOID
-Imm32GetSKT1TextMetric(_Out_ LPTEXTMETRICW lptm)
-{
-    HDC hDC;
-    HFONT hFont;
-    SIZE size;
-    HGDIOBJ hFontOld;
-    WCHAR szText[2] = { 0x894E, 0 };
-
-    hDC = GetDC(NULL);
-
-    ZeroMemory(&s_lfSKT1Font, sizeof(s_lfSKT1Font));
-    s_lfSKT1Font.lfHeight = -12;
-    s_lfSKT1Font.lfWeight = FW_NORMAL;
-    s_lfSKT1Font.lfCharSet = CHINESEBIG5_CHARSET;
-    s_lfSKT1Font.lfOutPrecision = OUT_TT_ONLY_PRECIS;
-    s_lfSKT1Font.lfClipPrecision = CLIP_DEFAULT_PRECIS;
-    s_lfSKT1Font.lfQuality = PROOF_QUALITY;
-    s_lfSKT1Font.lfPitchAndFamily = 49;
-    hFont = CreateFontIndirectW(&s_lfSKT1Font);
-
-    hFontOld = SelectObject(hDC, hFont);
-
-    GetTextMetricsW(hDC, lptm);
-
-    if (GetTextExtentPoint32W(hDC, szText, 1, &size) && lptm->tmMaxCharWidth < 
size.cx )
-        lptm->tmMaxCharWidth = size.cx;
-
-    DeleteObject(SelectObject(hDC, hFontOld));
-    ReleaseDC(NULL, hDC);
-}
-
-static VOID
+static void
 Imm32GetSoftKeyboardDimension(
     _In_ UINT uType,
     _Out_ LPINT pcx,
     _Out_ LPINT pcy)
 {
-    INT cxEdge, cyEdge;
-    TEXTMETRICW tm;
-
-    if (uType == 1)
+    if (uType == SOFTKEYBOARD_TYPE_T1)
     {
-        Imm32GetSKT1TextMetric(&tm);
-        *pcx = 15 * tm.tmMaxCharWidth + 2 * s_ptRaiseEdge.x + 139;
-        *pcy = 5 * tm.tmHeight + 2 * s_ptRaiseEdge.y + 58;
+        TEXTMETRICW tm;
+        T1_GetTextMetric(&tm);
+        *pcx = 15 * tm.tmMaxCharWidth + 2 * gptRaiseEdge.x + 139;
+        *pcy = 5 * tm.tmHeight + 2 * gptRaiseEdge.y + 58;
     }
     else
     {
-        cxEdge = GetSystemMetrics(SM_CXEDGE);
-        cyEdge = GetSystemMetrics(SM_CYEDGE);
+        INT cxEdge = GetSystemMetrics(SM_CXEDGE), cyEdge = 
GetSystemMetrics(SM_CXEDGE);
         *pcx = 2 * (GetSystemMetrics(SM_CXBORDER) + cxEdge) + 348;
         *pcy = 2 * (GetSystemMetrics(SM_CYBORDER) + cyEdge) + 136;
     }
@@ -205,65 +1211,85 @@ ImmCreateSoftKeyboard(
 {
     HKL hKL;
     PIMEDPI pImeDpi;
-    DWORD dwUICaps, style = (WS_POPUP | WS_DISABLED);
-    UINT i;
-    INT xSoftKBD, ySoftKBD, cxSoftKBD, cySoftKBD;
+    UINT iVK;
+    INT xSoftKBD, ySoftKBD, cxSoftKBD, cySoftKBD, cxEdge, cyEdge;
     HWND hwndSoftKBD;
+    DWORD Style, ExStyle, UICaps;
+    LPCWSTR pszClass;
+    RECT rcWorkArea;
 
-    TRACE("(%u, %p, %d, %d)\n", uType, hwndParent, x, y);
-
-    if (uType != 1 && uType != 2)
-        return 0;
+    if ((uType != SOFTKEYBOARD_TYPE_T1) && (uType != SOFTKEYBOARD_TYPE_C1))
+    {
+        ERR("uType: %u\n", uType);
+        return NULL; /* Invalid keyboard type */
+    }
 
+    /* Check IME */
     hKL = GetKeyboardLayout(0);
     pImeDpi = ImmLockImeDpi(hKL);
-    if (!pImeDpi)
-        return NULL;
+    if (IS_NULL_UNEXPECTEDLY(pImeDpi))
+        return NULL; /* No IME */
 
-    dwUICaps = pImeDpi->ImeInfo.fdwUICaps;
+    UICaps = pImeDpi->ImeInfo.fdwUICaps;
     ImmUnlockImeDpi(pImeDpi);
 
-    if (!(dwUICaps & UI_CAP_SOFTKBD))
-        return NULL;
-
-    if (s_bWannaInitSoftKBD)
+    /* Check IME capability */
+    if (!(UICaps & UI_CAP_SOFTKBD))
     {
-        if (!Imm32GetNearestMonitorSize(hwndParent, &s_rcWorkArea))
-            return NULL;
+        ERR("UICaps: 0x%X\n", UICaps);
+        return NULL; /* No capability for soft keyboard */
+    }
 
-        for (i = 0; i < 0xFF; ++i)
-            s_uScanCode[i] = MapVirtualKeyW(i, 0);
+    /* Want metrics? */
+    if (g_bWantSoftKBDMetrics)
+    {
+        for (iVK = 0; iVK < 0xFF; ++iVK)
+        {
+            guScanCode[iVK] = MapVirtualKeyW(iVK, 0);
+        }
 
-        s_ptRaiseEdge.x = GetSystemMetrics(SM_CXBORDER) + 
GetSystemMetrics(SM_CXEDGE);
-        s_ptRaiseEdge.y = GetSystemMetrics(SM_CYBORDER) + 
GetSystemMetrics(SM_CYEDGE);
+        cxEdge = GetSystemMetrics(SM_CXEDGE);
+        cyEdge = GetSystemMetrics(SM_CYEDGE);
+        gptRaiseEdge.x = GetSystemMetrics(SM_CXBORDER) + cxEdge;
+        gptRaiseEdge.y = GetSystemMetrics(SM_CYBORDER) + cyEdge;
 
-        s_bWannaInitSoftKBD = FALSE;
+        g_bWantSoftKBDMetrics = FALSE;
     }
 
+    if (!Imm32GetNearestWorkArea(hwndParent, &rcWorkArea))
+        return NULL;
+
+    /* Register the window class */
     if (!Imm32RegisterSoftKeyboard(uType))
+    {
+        ERR("\n");
         return NULL;
+    }
 
+    /* Calculate keyboard size */
     Imm32GetSoftKeyboardDimension(uType, &cxSoftKBD, &cySoftKBD);
 
-    xSoftKBD = max(s_rcWorkArea.left, min(x, s_rcWorkArea.right  - cxSoftKBD));
-    ySoftKBD = max(s_rcWorkArea.top,  min(y, s_rcWorkArea.bottom - cySoftKBD));
+    /* Adjust keyboard position */
+    xSoftKBD = Imm32Clamp(x, rcWorkArea.left, rcWorkArea.right  - cxSoftKBD);
+    ySoftKBD = Imm32Clamp(y, rcWorkArea.top , rcWorkArea.bottom - cySoftKBD);
 
-    if (uType == 1) /* Traditional Chinese */
+    /* Create soft keyboard window */
+    if (uType == SOFTKEYBOARD_TYPE_T1)
     {
-        hwndSoftKBD = CreateWindowExW(0,
-                                      L"SoftKBDClsT1", NULL, style,
-                                      xSoftKBD, ySoftKBD, cxSoftKBD, cySoftKBD,
-                                      hwndParent, NULL, ghImm32Inst, NULL);
+        Style = (WS_POPUP | WS_DISABLED);
+        ExStyle = 0;
+        pszClass = T1_CLASSNAMEW;
     }
-    else /* Simplified Chinese (uType == 2) */
+    else
     {
-        style |= WS_BORDER;
-        hwndSoftKBD = CreateWindowExW(WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME,
-                                      L"SoftKBDClsC1", NULL, style,
-                                      xSoftKBD, ySoftKBD, cxSoftKBD, cySoftKBD,
-                                      hwndParent, NULL, ghImm32Inst, NULL);
+        Style = (WS_POPUP | WS_DISABLED | WS_BORDER);
+        ExStyle = (WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME);
+        pszClass = C1_CLASSNAMEW;
     }
-
+    hwndSoftKBD = CreateWindowExW(ExStyle, pszClass, NULL, Style,
+                                  xSoftKBD, ySoftKBD, cxSoftKBD, cySoftKBD,
+                                  hwndParent, NULL, ghImm32Inst, NULL);
+    /* Initial is hidden */
     ShowWindow(hwndSoftKBD, SW_HIDE);
     UpdateWindow(hwndSoftKBD);
 
@@ -281,6 +1307,10 @@ ImmShowSoftKeyboard(
     _In_ INT nCmdShow)
 {
     TRACE("(%p, %d)\n", hwndSoftKBD, nCmdShow);
+
+    if (nCmdShow != SW_HIDE && nCmdShow != SW_SHOWNOACTIVATE)
+        WARN("nCmdShow %d is unexpected\n", nCmdShow);
+
     return hwndSoftKBD && ShowWindow(hwndSoftKBD, nCmdShow);
 }
 
diff --git a/dll/win32/imm32/t1keys.h b/dll/win32/imm32/t1keys.h
new file mode 100644
index 00000000000..22ed5b42469
--- /dev/null
+++ b/dll/win32/imm32/t1keys.h
@@ -0,0 +1,68 @@
+/*
+ * PROJECT:     ReactOS IMM32
+ * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
+ * PURPOSE:     Defining internal codes (T1K_...) of IME Soft Keyboard Type T1
+ * COPYRIGHT:   Copyright 2023 Katayama Hirofumi MZ 
<[email protected]>
+ */
+
+/* DEFINE_T1K(t1k_code, virtual_key_code, t1k_code_name, virtual_key_name, 
is_special) */
+DEFINE_T1K( 0, 0xC0, T1K_BACKTICK,   VK_OEM_3,      FALSE)
+DEFINE_T1K( 1, 0x31, T1K_1,          VK_1,          FALSE)
+DEFINE_T1K( 2, 0x32, T1K_2,          VK_2,          FALSE)
+DEFINE_T1K( 3, 0x33, T1K_3,          VK_3,          FALSE)
+DEFINE_T1K( 4, 0x34, T1K_4,          VK_4,          FALSE)
+DEFINE_T1K( 5, 0x35, T1K_5,          VK_5,          FALSE)
+DEFINE_T1K( 6, 0x36, T1K_6,          VK_6,          FALSE)
+DEFINE_T1K( 7, 0x37, T1K_7,          VK_7,          FALSE)
+DEFINE_T1K( 8, 0x38, T1K_8,          VK_8,          FALSE)
+DEFINE_T1K( 9, 0x39, T1K_9,          VK_9,          FALSE)
+DEFINE_T1K(10, 0x30, T1K_0,          VK_0,          FALSE)
+DEFINE_T1K(11, 0xBD, T1K_MINUS,      VK_OEM_MINUS,  FALSE)
+DEFINE_T1K(12, 0xBB, T1K_PLUS,       VK_OEM_PLUS,   FALSE)
+DEFINE_T1K(13, 0xDC, T1K_OEM_5,      VK_OEM_5,      FALSE)
+DEFINE_T1K(14, 0x51, T1K_Q,          VK_Q,          FALSE)
+DEFINE_T1K(15, 0x57, T1K_W,          VK_W,          FALSE)
+DEFINE_T1K(16, 0x45, T1K_E,          VK_E,          FALSE)
+DEFINE_T1K(17, 0x52, T1K_R,          VK_R,          FALSE)
+DEFINE_T1K(18, 0x54, T1K_T,          VK_T,          FALSE)
+DEFINE_T1K(19, 0x59, T1K_Y,          VK_Y,          FALSE)
+DEFINE_T1K(20, 0x55, T1K_U,          VK_U,          FALSE)
+DEFINE_T1K(21, 0x49, T1K_I,          VK_I,          FALSE)
+DEFINE_T1K(22, 0x4F, T1K_O,          VK_O,          FALSE)
+DEFINE_T1K(23, 0x50, T1K_P,          VK_P,          FALSE)
+DEFINE_T1K(24, 0xDB, T1K_OEM_4,      VK_OEM_4,      FALSE)
+DEFINE_T1K(25, 0xDD, T1K_OEM_6,      VK_OEM_6,      FALSE)
+DEFINE_T1K(26, 0x41, T1K_A,          VK_A,          FALSE)
+DEFINE_T1K(27, 0x53, T1K_S,          VK_S,          FALSE)
+DEFINE_T1K(28, 0x44, T1K_D,          VK_D,          FALSE)
+DEFINE_T1K(29, 0x46, T1K_F,          VK_F,          FALSE)
+DEFINE_T1K(30, 0x47, T1K_G,          VK_G,          FALSE)
+DEFINE_T1K(31, 0x48, T1K_H,          VK_H,          FALSE)
+DEFINE_T1K(32, 0x4A, T1K_J,          VK_J,          FALSE)
+DEFINE_T1K(33, 0x4B, T1K_K,          VK_K,          FALSE)
+DEFINE_T1K(34, 0x4C, T1K_L,          VK_L,          FALSE)
+DEFINE_T1K(35, 0xBA, T1K_OEM_1,      VK_OEM_1,      FALSE)
+DEFINE_T1K(36, 0xDE, T1K_OEM_7,      VK_OEM_7,      FALSE)
+DEFINE_T1K(37, 0x5A, T1K_Z,          VK_Z,          FALSE)
+DEFINE_T1K(38, 0x58, T1K_X,          VK_X,          FALSE)
+DEFINE_T1K(39, 0x43, T1K_C,          VK_C,          FALSE)
+DEFINE_T1K(40, 0x56, T1K_V,          VK_V,          FALSE)
+DEFINE_T1K(41, 0x42, T1K_B,          VK_B,          FALSE)
+DEFINE_T1K(42, 0x4E, T1K_N,          VK_N,          FALSE)
+DEFINE_T1K(43, 0x4D, T1K_M,          VK_M,          FALSE)
+DEFINE_T1K(44, 0xBC, T1K_OEM_COMMA,  VK_OEM_COMMA,  FALSE)
+DEFINE_T1K(45, 0xBE, T1K_OEM_PERIOD, VK_OEM_PERIOD, FALSE)
+DEFINE_T1K(46, 0xBF, T1K_OEM_2,      VK_OEM_2,      FALSE)
+DEFINE_T1K(47, 0x08, T1K_BACKSPACE,  VK_BACK,       TRUE)
+DEFINE_T1K(48, 0x09, T1K_TAB,        VK_TAB,        TRUE)
+DEFINE_T1K(49, 0x14, T1K_CAPS,       VK_CAPITAL,    TRUE)
+DEFINE_T1K(50, 0x0D, T1K_ENTER,      VK_RETURN,     TRUE)
+DEFINE_T1K(51, 0x10, T1K_L_SHIFT,    VK_SHIFT,      TRUE)
+DEFINE_T1K(52, 0x10, T1K_R_SHIFT,    VK_SHIFT,      TRUE)
+DEFINE_T1K(53, 0x11, T1K_L_CTRL,     VK_CONTROL,    TRUE)
+DEFINE_T1K(54, 0x11, T1K_R_CTRL,     VK_CONTROL,    TRUE)
+DEFINE_T1K(55, 0x12, T1K_L_ALT,      VK_MENU,       TRUE)
+DEFINE_T1K(56, 0x12, T1K_R_ALT,      VK_MENU,       TRUE)
+DEFINE_T1K(57, 0x1B, T1K_ESCAPE,     VK_ESCAPE,     TRUE)
+DEFINE_T1K(58, 0x20, T1K_SPACE,      VK_SPACE,      TRUE)
+DEFINE_T1K(59, 0x00, T1K_NONE,       0,             TRUE)
diff --git a/sdk/include/ddk/immdev.h b/sdk/include/ddk/immdev.h
index 619ac609367..8a8586b774d 100644
--- a/sdk/include/ddk/immdev.h
+++ b/sdk/include/ddk/immdev.h
@@ -17,12 +17,23 @@
 extern "C" {
 #endif
 
+typedef struct tagSOFTKBDDATA
+{
+    UINT uCount;
+    WORD wCode[1][256];
+} SOFTKBDDATA, *PSOFTKBDDATA, *LPSOFTKBDDATA;
+
 /* wParam for WM_IME_CONTROL */
 #define IMC_GETCONVERSIONMODE           0x0001
 #define IMC_GETSENTENCEMODE             0x0003
 #define IMC_GETOPENSTATUS               0x0005
+#define IMC_GETSOFTKBDFONT              0x0011
+#define IMC_SETSOFTKBDFONT              0x0012
 #define IMC_GETSOFTKBDPOS               0x0013
 #define IMC_SETSOFTKBDPOS               0x0014
+#define IMC_GETSOFTKBDSUBTYPE           0x0015
+#define IMC_SETSOFTKBDSUBTYPE           0x0016
+#define IMC_SETSOFTKBDDATA              0x0018
 
 /* wParam for WM_IME_SYSTEM */
 #define IMS_NOTIFYIMESHOW       0x05
@@ -46,6 +57,9 @@ extern "C" {
 #define IMS_SETLANGBAND         0x23
 #define IMS_UNSETLANGBAND       0x24
 
+/* wParam for WM_IME_NOTIFY */
+#define IMN_SOFTKBDDESTROYED    0x0011
+
 #define IMMGWL_IMC       0
 #define IMMGWL_PRIVATE   (sizeof(LONG))
 
@@ -59,7 +73,7 @@ typedef struct _tagINPUTCONTEXT {
     POINT               ptSoftKbdPos;
     DWORD               fdwConversion;
     DWORD               fdwSentence;
-    union   {
+    union {
         LOGFONTA        A;
         LOGFONTW        W;
     } lfFont;

Reply via email to