xref: /reactos/dll/win32/imm32/softkbd.c (revision 67830618)
1 /*
2  * PROJECT:     ReactOS IMM32
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     Implementing IME Soft Keyboard
5  * COPYRIGHT:   Copyright 2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6  */
7 
8 #include "precomp.h"
9 #include "resource.h"
10 
11 WINE_DEFAULT_DEBUG_CHANNEL(imm);
12 
13 /*
14  * There are two types of IME Soft Keyboard: Type T1 and Type C1.
15  * T1 is created for Traditional Chinese but not limitted to it.
16  * C1 is created for Simplified Chinese but not limitted to it.
17  * Type C1 has SHIFT status while Type T1 hasn't.
18  */
19 
20 static UINT guScanCode[256]; /* Mapping: virtual key --> scan code */
21 static POINT gptRaiseEdge; /* Border + Edge metrics */
22 static BOOL g_bWantSoftKBDMetrics = TRUE;
23 
24 static inline BOOL
Imm32PtInRect(_In_ const POINT * ppt,_In_ LONG x,_In_ LONG y,_In_ LONG cx,_In_ LONG cy)25 Imm32PtInRect(
26     _In_ const POINT *ppt,
27     _In_ LONG x,
28     _In_ LONG y,
29     _In_ LONG cx,
30     _In_ LONG cy)
31 {
32     return (x <= ppt->x) && (ppt->x < x + cx) && (y <= ppt->y) && (ppt->y < y + cy);
33 }
34 
35 static void
Imm32DrawBitmap(_In_ HDC hdc,_In_ INT x,_In_ INT y,_In_ INT width,_In_ INT height,_In_ INT nBitmapID)36 Imm32DrawBitmap(
37     _In_ HDC hdc,
38     _In_ INT x,
39     _In_ INT y,
40     _In_ INT width,
41     _In_ INT height,
42     _In_ INT nBitmapID)
43 {
44     HBITMAP hBitmap = LoadBitmapW(ghImm32Inst, MAKEINTRESOURCEW(nBitmapID));
45     HDC hMemDC = CreateCompatibleDC(hdc);
46     HGDIOBJ hbmOld = SelectObject(hMemDC, hBitmap);
47     BitBlt(hdc, x, y, width, height, hMemDC, 0, 0, SRCCOPY);
48     DeleteObject(SelectObject(hMemDC, hbmOld));
49     DeleteDC(hMemDC);
50 }
51 
52 static inline INT
Imm32Clamp(_In_ INT x,_In_ INT xMin,_In_ INT xMax)53 Imm32Clamp(
54     _In_ INT x,
55     _In_ INT xMin,
56     _In_ INT xMax)
57 {
58     if (x < xMin)
59         return xMin;
60     if (x > xMax)
61         return xMax;
62     return x;
63 }
64 
65 static VOID
Imm32GetAllMonitorSize(_Out_ LPRECT prcWork)66 Imm32GetAllMonitorSize(
67     _Out_ LPRECT prcWork)
68 {
69     if (GetSystemMetrics(SM_CMONITORS) == 1)
70     {
71         SystemParametersInfoW(SPI_GETWORKAREA, 0, prcWork, 0);
72         return;
73     }
74 
75     prcWork->left   = GetSystemMetrics(SM_XVIRTUALSCREEN);
76     prcWork->top    = GetSystemMetrics(SM_YVIRTUALSCREEN);
77     prcWork->right  = prcWork->left + GetSystemMetrics(SM_CXVIRTUALSCREEN);
78     prcWork->bottom = prcWork->top  + GetSystemMetrics(SM_CYVIRTUALSCREEN);
79 }
80 
81 static BOOL
Imm32GetNearestWorkArea(_In_opt_ HWND hwnd,_Out_ LPRECT prcWork)82 Imm32GetNearestWorkArea(
83     _In_opt_ HWND hwnd,
84     _Out_ LPRECT prcWork)
85 {
86     HMONITOR hMonitor;
87     MONITORINFO mi;
88 
89     if (GetSystemMetrics(SM_CMONITORS) == 1)
90     {
91         Imm32GetAllMonitorSize(prcWork);
92         return TRUE;
93     }
94 
95     hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
96     if (!hMonitor)
97     {
98         ERR("hwnd: %p\n", hwnd);
99         return FALSE;
100     }
101 
102     ZeroMemory(&mi, sizeof(mi));
103     mi.cbSize = sizeof(mi);
104     GetMonitorInfoW(hMonitor, &mi);
105     *prcWork = mi.rcWork;
106     return TRUE;
107 }
108 
109 /*****************************************************************************
110  * IME Soft Keyboard Type T1
111  */
112 
113 #define T1_CLASSNAMEW L"SoftKBDClsT1"
114 
115 #undef DEFINE_T1K
116 #define DEFINE_T1K(t1k_code, virtual_key_code, t1k_code_name, virtual_key_name, is_special) \
117     t1k_code_name = t1k_code,
118 
119 /* Define T1 internal codes (T1K_...) */
120 typedef enum T1KEY
121 {
122 #include "t1keys.h"
123 } T1KEY;
124 
125 #undef DEFINE_T1K
126 #define DEFINE_T1K(t1k_code, virtual_key_code, t1k_code_name, virtual_key_name, is_special) \
127     virtual_key_code,
128 
129 #define T1K_MAX 60
130 
131 /* Mapping: T1K --> Virtual Key */
132 const BYTE gT1K2VK[T1K_MAX] =
133 {
134 #include "t1keys.h"
135 };
136 
137 typedef struct T1WINDOW
138 {
139     INT cxDefWidth;           /* Regular key width */
140     INT cxWidth47;            /* [BackSpace] width */
141     INT cxWidth48;            /* [Tab] width */
142     INT cxWidth49;            /* [Caps] width */
143     INT cxWidth50;            /* [Enter] width */
144     INT cxWidth51or52;        /* [Shift] width */
145     INT cxWidth53or54;        /* [Ctrl] width */
146     INT cxWidth55or56;        /* [Alt] width */
147     INT cxWidth57;            /* [Esc] width */
148     INT cxWidth58;            /* [Space] width */
149     INT cyDefHeight;          /* Regular key height */
150     INT cyHeight50;           /* [Enter] height */
151     POINT KeyPos[T1K_MAX];    /* T1K --> POINT */
152     WCHAR chKeyChar[48];      /* T1K --> WCHAR */
153     HBITMAP hbmKeyboard;      /* The keyboard image */
154     DWORD CharSet;            /* LOGFONT.lfCharSet */
155     UINT PressedKey;          /* Currently pressed key */
156     POINT pt0, pt1;           /* The soft keyboard window position */
157     LPARAM KeyboardSubType;   /* See IMC_GETSOFTKBDSUBTYPE/IMC_SETSOFTKBDSUBTYPE */
158 } T1WINDOW, *PT1WINDOW;
159 
160 #define T1_KEYPOS(iKey) pT1->KeyPos[iKey]
161 
162 static LOGFONTW g_T1LogFont;
163 
164 static void
T1_GetTextMetric(_Out_ LPTEXTMETRICW ptm)165 T1_GetTextMetric(_Out_ LPTEXTMETRICW ptm)
166 {
167     WCHAR wch;
168     SIZE textSize;
169     HFONT hFont;
170     HGDIOBJ hFontOld;
171     HDC hDC;
172 #ifndef NDEBUG
173     WCHAR szFace[LF_FACESIZE];
174 #endif
175 
176     ZeroMemory(&g_T1LogFont, sizeof(g_T1LogFont));
177     g_T1LogFont.lfHeight = -12;
178     g_T1LogFont.lfWeight = FW_NORMAL;
179     g_T1LogFont.lfCharSet = CHINESEBIG5_CHARSET;
180 #ifdef NO_HACK /* FIXME: We lack proper Asian fonts! */
181     g_T1LogFont.lfOutPrecision = OUT_TT_ONLY_PRECIS;
182     g_T1LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
183     g_T1LogFont.lfQuality = PROOF_QUALITY;
184     g_T1LogFont.lfPitchAndFamily = FF_MODERN | FIXED_PITCH;
185 #else
186     StringCchCopyW(g_T1LogFont.lfFaceName, _countof(g_T1LogFont.lfFaceName), L"MS Shell Dlg");
187 #endif
188     hFont = CreateFontIndirectW(&g_T1LogFont);
189 
190     hDC = GetDC(NULL);
191     hFontOld = SelectObject(hDC, hFont);
192 
193 #ifndef NDEBUG
194     GetTextFaceW(hDC, _countof(szFace), szFace);
195     TRACE("szFace: %s\n", debugstr_w(szFace));
196 #endif
197 
198     GetTextMetricsW(hDC, ptm);
199 
200     wch = 0x4E11; /* U+4E11: 丑 */
201     if (GetTextExtentPoint32W(hDC, &wch, 1, &textSize) && textSize.cx > ptm->tmMaxCharWidth)
202         ptm->tmMaxCharWidth = textSize.cx;
203 
204     DeleteObject(SelectObject(hDC, hFontOld));
205     ReleaseDC(NULL, hDC);
206 }
207 
208 static void
T1_InitButtonPos(_Out_ PT1WINDOW pT1)209 T1_InitButtonPos(_Out_ PT1WINDOW pT1)
210 {
211     TEXTMETRICW tm;
212     LONG cxLarge, cyLarge;
213     LONG xKey1, yKey1, xKey2, yKey2, xKey3, yKey3;
214     LONG yKey4, xKey4, xKey5, yKey5, xKey6, xKey7;
215     INT iKey;
216 
217     T1_GetTextMetric(&tm);
218 
219     cxLarge = (3 * tm.tmMaxCharWidth + 18) / 2;
220     cyLarge = tm.tmHeight + 8;
221 
222     /* key widths and heights */
223     pT1->cxDefWidth = (2 * tm.tmMaxCharWidth + 12) / 2;
224     pT1->cxWidth47 = (2 * tm.tmMaxCharWidth + 12) / 2 + 1;
225     pT1->cxWidth49 = (4 * tm.tmMaxCharWidth + 24) / 2 + 3;
226     pT1->cxWidth51or52 = (5 * tm.tmMaxCharWidth + 30) / 2 + 5;
227     pT1->cxWidth58 = 4 * (3 * tm.tmMaxCharWidth + 18) / 2 + 15;
228     pT1->cxWidth48 = pT1->cxWidth50 = cxLarge + 2;
229     pT1->cxWidth53or54 = pT1->cxWidth55or56 = cxLarge + 2;
230     pT1->cyHeight50 = 2 * (tm.tmHeight + 8) + 3;
231     pT1->cxWidth57 = cxLarge + 1;
232     pT1->cyDefHeight = cyLarge;
233 
234     /* First row */
235     xKey1 = gptRaiseEdge.x + 3;
236     yKey1 = gptRaiseEdge.y + 3;
237     for (iKey = 0; iKey < T1K_Q; ++iKey)
238     {
239         T1_KEYPOS(iKey).x = xKey1;
240         T1_KEYPOS(iKey).y = yKey1;
241         xKey1 += pT1->cxDefWidth + 3;
242     }
243     T1_KEYPOS(T1K_BACKSPACE).y = yKey1;
244     T1_KEYPOS(T1K_BACKSPACE).x = xKey1;
245 
246     /* 2nd row */
247     xKey2 = 3 + gptRaiseEdge.x + pT1->cxWidth48 + 3;
248     yKey2 = 3 + yKey1 + cyLarge;
249     T1_KEYPOS(T1K_TAB).x = gptRaiseEdge.x + 3;
250     T1_KEYPOS(T1K_TAB).y = yKey2;
251     for (iKey = T1K_Q; iKey < T1K_A; ++iKey)
252     {
253         T1_KEYPOS(iKey).x = xKey2;
254         T1_KEYPOS(iKey).y = yKey2;
255         xKey2 += pT1->cxDefWidth + 3;
256     }
257     T1_KEYPOS(T1K_ENTER).x = xKey2;
258     T1_KEYPOS(T1K_ENTER).y = yKey2;
259 
260     /* 3rd row */
261     xKey3 = gptRaiseEdge.x + 3 + pT1->cxWidth49 + 3;
262     yKey3 = yKey2 + cyLarge + 3;
263     T1_KEYPOS(T1K_CAPS).x = gptRaiseEdge.x + 3;
264     T1_KEYPOS(T1K_CAPS).y = yKey3;
265     for (iKey = T1K_A; iKey < T1K_Z; ++iKey)
266     {
267         T1_KEYPOS(iKey).x = xKey3;
268         T1_KEYPOS(iKey).y = yKey3;
269         xKey3 += pT1->cxDefWidth + 3;
270     }
271 
272     /* 4th row */
273     xKey4 = gptRaiseEdge.x + pT1->cxWidth51or52 + 3 + 3;
274     yKey4 = yKey3 + cyLarge + 3;
275     T1_KEYPOS(T1K_L_SHIFT).x = gptRaiseEdge.x + 3;
276     T1_KEYPOS(T1K_L_SHIFT).y = yKey4;
277     for (iKey = T1K_Z; iKey < T1K_BACKSPACE; ++iKey)
278     {
279         T1_KEYPOS(iKey).x = xKey4;
280         T1_KEYPOS(iKey).y = yKey4;
281         xKey4 += pT1->cxDefWidth + 3;
282     }
283     T1_KEYPOS(T1K_R_SHIFT).x = xKey4;
284     T1_KEYPOS(T1K_R_SHIFT).y = yKey4;
285 
286     /* 5th row */
287     xKey5 = gptRaiseEdge.x + 3 + pT1->cxWidth53or54 + 3;
288     T1_KEYPOS(T1K_L_CTRL).x = gptRaiseEdge.x + 3;
289     T1_KEYPOS(T1K_ESCAPE).x = xKey5;
290     T1_KEYPOS(T1K_L_ALT).x = xKey5 + pT1->cxWidth57 + 3;
291 
292     yKey5 = yKey4 + cyLarge + 3;
293     T1_KEYPOS(T1K_L_CTRL).y = T1_KEYPOS(T1K_ESCAPE).y = T1_KEYPOS(T1K_L_ALT).y = yKey5;
294     T1_KEYPOS(T1K_R_ALT).y = T1_KEYPOS(T1K_SPACE).y = T1_KEYPOS(T1K_R_CTRL).y = yKey5;
295 
296     xKey6 = xKey5 + pT1->cxWidth57 + 3 + pT1->cxWidth55or56 + 3;
297     T1_KEYPOS(T1K_SPACE).x = xKey6;
298 
299     xKey7 = xKey6 + pT1->cxWidth58 + 3;
300     T1_KEYPOS(T1K_R_ALT).x = xKey7;
301     T1_KEYPOS(T1K_R_CTRL).x = xKey7 + pT1->cxWidth57 + pT1->cxWidth55or56 + 6;
302 }
303 
304 /* Draw keyboard key edge */
305 static void
T1_DrawConvexRect(_In_ HDC hDC,_In_ INT x,_In_ INT y,_In_ INT width,_In_ INT height)306 T1_DrawConvexRect(
307     _In_ HDC hDC,
308     _In_ INT x,
309     _In_ INT y,
310     _In_ INT width,
311     _In_ INT height)
312 {
313     HGDIOBJ hBlackPen = GetStockObject(BLACK_PEN);
314     HGDIOBJ hLtGrayBrush = GetStockObject(LTGRAY_BRUSH);
315     HGDIOBJ hGrayBrush = GetStockObject(GRAY_BRUSH);
316     INT dx = width + 4, dy = height + 4;
317     INT x0 = x - 2, y0 = y + height + 2;
318 
319     /* Face */
320     SelectObject(hDC, hBlackPen);
321     SelectObject(hDC, hLtGrayBrush);
322     Rectangle(hDC, x0, y - 2, x0 + dx, y0);
323 
324     /* Rounded corners */
325     PatBlt(hDC, x0, y - 2, 1, 1, PATCOPY);
326     PatBlt(hDC, x0, y0, 1, -1, PATCOPY);
327     PatBlt(hDC, x0 + dx, y - 2, -1, 1, PATCOPY);
328     PatBlt(hDC, x0 + dx, y0, -1, -1, PATCOPY);
329 
330     /* Light edge */
331     PatBlt(hDC, x0 + 1, y + dy - 3, 1, 2 - dy, WHITENESS);
332     PatBlt(hDC, x0 + 1, y - 1, dx - 2, 1, WHITENESS);
333 
334     /* Dark edge */
335     SelectObject(hDC, hGrayBrush);
336     PatBlt(hDC, x0 + 1, y + dy - 3, dx - 2, -1, PATCOPY);
337     PatBlt(hDC, x0 + dx - 1, y + dy - 3, -1, 2 - dy, PATCOPY);
338 }
339 
340 static void
T1_DrawLabels(_In_ HDC hDC,_In_ const T1WINDOW * pT1,_In_ LPCWSTR pszBmpName)341 T1_DrawLabels(
342     _In_ HDC hDC,
343     _In_ const T1WINDOW *pT1,
344     _In_ LPCWSTR pszBmpName)
345 {
346     HBITMAP hBitmap = LoadBitmapW(ghImm32Inst, pszBmpName);
347     HDC hdcMem = CreateCompatibleDC(hDC);
348     HGDIOBJ hbmOld = SelectObject(hdcMem, hBitmap);
349     INT iKey;
350     for (iKey = 0; iKey < T1K_BACKSPACE; ++iKey)
351     {
352         const POINT *ppt = &T1_KEYPOS(iKey);
353         BitBlt(hDC, ppt->x, ppt->y, 8, 8, hdcMem, iKey * 8, 0, SRCCOPY);
354     }
355     SelectObject(hdcMem, hbmOld);
356     DeleteDC(hdcMem);
357     DeleteObject(hBitmap);
358 }
359 
360 static void
T1_InitBitmap(_In_ HWND hWnd,_Inout_ PT1WINDOW pT1)361 T1_InitBitmap(
362     _In_ HWND hWnd,
363     _Inout_ PT1WINDOW pT1)
364 {
365     HDC hDC, hMemDC;
366     HGDIOBJ hNullPen = GetStockObject(NULL_PEN), hbrLtGray = GetStockObject(LTGRAY_BRUSH);
367     RECT rc;
368     INT iKey;
369 
370     /* Create the bitmap */
371     hDC = GetDC(hWnd);
372     hMemDC = CreateCompatibleDC(hDC);
373     GetClientRect(hWnd, &rc);
374     pT1->hbmKeyboard = CreateCompatibleBitmap(hDC, rc.right - rc.left, rc.bottom - rc.top);
375     ReleaseDC(hWnd, hDC);
376 
377     /* Draw keyboard face */
378     SelectObject(hMemDC, pT1->hbmKeyboard);
379     SelectObject(hMemDC, hNullPen);
380     SelectObject(hMemDC, hbrLtGray);
381     Rectangle(hMemDC, rc.left, rc.top, rc.right + 1, rc.bottom + 1);
382     DrawEdge(hMemDC, &rc, EDGE_RAISED, BF_RECT);
383 
384     /* 53 --> Left [Ctrl] */
385     T1_DrawConvexRect(hMemDC,
386                       T1_KEYPOS(T1K_L_CTRL).x, T1_KEYPOS(T1K_L_CTRL).y,
387                       pT1->cxWidth53or54, pT1->cyDefHeight);
388     Imm32DrawBitmap(hMemDC,
389                     pT1->cxWidth53or54 / 2 + T1_KEYPOS(T1K_L_CTRL).x - 8,
390                     pT1->cyDefHeight   / 2 + T1_KEYPOS(T1K_L_CTRL).y - 4,
391                     16, 9, IDB_T1_CTRL);
392 
393     /* 54 --> Right [Ctrl] */
394     T1_DrawConvexRect(hMemDC,
395                       T1_KEYPOS(T1K_R_CTRL).x, T1_KEYPOS(T1K_R_CTRL).y,
396                       pT1->cxWidth53or54, pT1->cyDefHeight);
397     Imm32DrawBitmap(hMemDC,
398                     pT1->cxWidth53or54 / 2 + T1_KEYPOS(T1K_R_CTRL).x - 8,
399                     pT1->cyDefHeight   / 2 + T1_KEYPOS(T1K_R_CTRL).y - 4,
400                     16, 9, IDB_T1_CTRL);
401 
402     /* 57 --> [Esc] */
403     T1_DrawConvexRect(hMemDC,
404                       T1_KEYPOS(T1K_ESCAPE).x, T1_KEYPOS(T1K_ESCAPE).y,
405                       pT1->cxWidth57, pT1->cyDefHeight);
406     Imm32DrawBitmap(hMemDC,
407                     pT1->cxWidth57   / 2 + T1_KEYPOS(T1K_ESCAPE).x - 9,
408                     pT1->cyDefHeight / 2 + T1_KEYPOS(T1K_ESCAPE).y - 4,
409                     18, 9, IDB_T1_ESCAPE);
410 
411     /* 55 --> Left [Alt] */
412     T1_DrawConvexRect(hMemDC,
413                       T1_KEYPOS(T1K_L_ALT).x, T1_KEYPOS(T1K_L_ALT).y,
414                       pT1->cxWidth55or56, pT1->cyDefHeight);
415     Imm32DrawBitmap(hMemDC,
416                     pT1->cxWidth55or56 / 2 + T1_KEYPOS(T1K_L_ALT).x - 8,
417                     pT1->cyDefHeight   / 2 + T1_KEYPOS(T1K_L_ALT).y - 4,
418                     16, 9, IDB_T1_ALT);
419 
420     /* 56 --> Right [Alt] */
421     T1_DrawConvexRect(hMemDC,
422                       T1_KEYPOS(T1K_R_ALT).x, T1_KEYPOS(T1K_R_ALT).y,
423                       pT1->cxWidth55or56, pT1->cyDefHeight);
424     Imm32DrawBitmap(hMemDC,
425                     pT1->cxWidth55or56 / 2 + T1_KEYPOS(T1K_R_ALT).x - 8,
426                     pT1->cyDefHeight   / 2 + T1_KEYPOS(T1K_R_ALT).y - 4,
427                     16, 9, IDB_T1_ALT);
428 
429     /* 58 --> [Space] */
430     T1_DrawConvexRect(hMemDC,
431                       T1_KEYPOS(T1K_SPACE).x, T1_KEYPOS(T1K_SPACE).y,
432                       pT1->cxWidth58, pT1->cyDefHeight);
433 
434     /* 51 --> Left [Shift] */
435     T1_DrawConvexRect(hMemDC,
436                       T1_KEYPOS(T1K_L_SHIFT).x, T1_KEYPOS(T1K_L_SHIFT).y,
437                       pT1->cxWidth51or52, pT1->cyDefHeight);
438     Imm32DrawBitmap(hMemDC,
439                     pT1->cxWidth51or52 / 2 + T1_KEYPOS(T1K_L_SHIFT).x - 11,
440                     pT1->cyDefHeight   / 2 + T1_KEYPOS(T1K_L_SHIFT).y - 4,
441                     23, 9, IDB_T1_SHIFT);
442 
443     /* 52 --> Right [Shift] */
444     T1_DrawConvexRect(hMemDC,
445                       T1_KEYPOS(T1K_R_SHIFT).x, T1_KEYPOS(T1K_R_SHIFT).y,
446                       pT1->cxWidth51or52, pT1->cyDefHeight);
447     Imm32DrawBitmap(hMemDC,
448                     pT1->cxWidth51or52 / 2 + T1_KEYPOS(T1K_R_SHIFT).x - 11,
449                     pT1->cyDefHeight   / 2 + T1_KEYPOS(T1K_R_SHIFT).y - 4,
450                     23, 9, IDB_T1_SHIFT);
451 
452     /* 49 --> [Caps] */
453     T1_DrawConvexRect(hMemDC,
454                       T1_KEYPOS(T1K_CAPS).x, T1_KEYPOS(T1K_CAPS).y,
455                       pT1->cxWidth49, pT1->cyDefHeight);
456     Imm32DrawBitmap(hMemDC,
457                     pT1->cxWidth49   / 2 + T1_KEYPOS(T1K_CAPS).x - 11,
458                     pT1->cyDefHeight / 2 + T1_KEYPOS(T1K_CAPS).y - 4,
459                     22, 9, IDB_T1_CAPS);
460 
461     /* 48 --> [Tab] */
462     T1_DrawConvexRect(hMemDC,
463                       T1_KEYPOS(T1K_TAB).x, T1_KEYPOS(T1K_TAB).y,
464                       pT1->cxWidth48, pT1->cyDefHeight);
465     Imm32DrawBitmap(hMemDC,
466                     pT1->cxWidth48   / 2 + T1_KEYPOS(T1K_TAB).x - 8,
467                     pT1->cyDefHeight / 2 + T1_KEYPOS(T1K_TAB).y - 4,
468                     16, 9, IDB_T1_TAB);
469 
470     /* 50 --> [Enter] */
471     T1_DrawConvexRect(hMemDC,
472                       T1_KEYPOS(T1K_ENTER).x, T1_KEYPOS(T1K_ENTER).y,
473                       pT1->cxWidth50, pT1->cyHeight50);
474     Imm32DrawBitmap(hMemDC,
475                     pT1->cxWidth50  / 2 + T1_KEYPOS(T1K_ENTER).x - 13,
476                     pT1->cyHeight50 / 2 + T1_KEYPOS(T1K_ENTER).y - 4,
477                     26, 9, IDB_T1_ENTER);
478 
479     /* 47 --> [BackSpace] */
480     T1_DrawConvexRect(hMemDC,
481                       T1_KEYPOS(T1K_BACKSPACE).x, T1_KEYPOS(T1K_BACKSPACE).y,
482                       pT1->cxWidth47, pT1->cyDefHeight);
483     Imm32DrawBitmap(hMemDC,
484                     pT1->cxWidth47   / 2 + T1_KEYPOS(T1K_BACKSPACE).x - 8,
485                     pT1->cyDefHeight / 2 + T1_KEYPOS(T1K_BACKSPACE).y - 4,
486                     16, 9, IDB_T1_BACKSPACE);
487 
488     /* Regular keys */
489     for (iKey = 0; iKey < T1K_BACKSPACE; ++iKey)
490     {
491         LPPOINT ppt = &T1_KEYPOS(iKey);
492         T1_DrawConvexRect(hMemDC, ppt->x, ppt->y, pT1->cxDefWidth, pT1->cyDefHeight);
493     }
494 
495     T1_DrawLabels(hMemDC, pT1, MAKEINTRESOURCEW(IDB_T1_CHARS));
496     DeleteDC(hMemDC);
497 }
498 
499 static INT
T1_OnCreate(_In_ HWND hWnd)500 T1_OnCreate(
501     _In_ HWND hWnd)
502 {
503     PT1WINDOW pT1;
504     HGLOBAL hGlobal = GlobalAlloc(GHND, sizeof(T1WINDOW));
505     if (!hGlobal)
506         return -1;
507 
508     pT1 = (PT1WINDOW)GlobalLock(hGlobal);
509     if (!pT1)
510     {
511         GlobalFree(hGlobal);
512         return -1;
513     }
514 
515     SetWindowLongPtrW(hWnd, 0, (LONG_PTR)hGlobal);
516     pT1->pt1.x = pT1->pt1.y = -1;
517     pT1->PressedKey = T1K_NONE;
518     pT1->CharSet = CHINESEBIG5_CHARSET;
519 
520     T1_InitButtonPos(pT1);
521     T1_InitBitmap(hWnd, pT1);
522     GlobalUnlock(hGlobal);
523 
524     return 0;
525 }
526 
527 static void
T1_DrawDragBorder(_In_ HWND hWnd,_In_ const POINT * ppt1,_In_ const POINT * ppt2)528 T1_DrawDragBorder(
529     _In_ HWND hWnd,
530     _In_ const POINT *ppt1,
531     _In_ const POINT *ppt2)
532 {
533     INT cxBorder = GetSystemMetrics(SM_CXBORDER), cyBorder = GetSystemMetrics(SM_CYBORDER);
534     INT x = ppt1->x - ppt2->x, y = ppt1->y - ppt2->y;
535     HGDIOBJ hGrayBrush = GetStockObject(GRAY_BRUSH);
536     RECT rc;
537     HDC hDisplayDC;
538 
539     GetWindowRect(hWnd, &rc);
540     hDisplayDC = CreateDCW(L"DISPLAY", NULL, NULL, NULL);
541     SelectObject(hDisplayDC, hGrayBrush);
542     PatBlt(hDisplayDC, x, y, rc.right - rc.left - cxBorder, cyBorder, PATINVERT);
543     PatBlt(hDisplayDC, x, cyBorder + y, cxBorder, rc.bottom - rc.top - cyBorder, PATINVERT);
544     PatBlt(hDisplayDC, x + cxBorder, y + rc.bottom - rc.top, rc.right - rc.left - cxBorder, -cyBorder, PATINVERT);
545     PatBlt(hDisplayDC, x + rc.right - rc.left, y, -cxBorder, rc.bottom - rc.top - cyBorder, PATINVERT);
546     DeleteDC(hDisplayDC);
547 }
548 
549 static void
T1_OnDestroy(_In_ HWND hWnd)550 T1_OnDestroy(
551     _In_ HWND hWnd)
552 {
553     HGLOBAL hGlobal;
554     PT1WINDOW pT1;
555     HWND hwndOwner;
556 
557     hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
558     pT1 = (PT1WINDOW)GlobalLock(hGlobal);
559     if (!hGlobal || !pT1)
560         return;
561 
562     if (pT1->pt1.x != -1 && pT1->pt1.y != -1)
563         T1_DrawDragBorder(hWnd, &pT1->pt0, &pT1->pt1);
564 
565     DeleteObject(pT1->hbmKeyboard);
566     GlobalUnlock(hGlobal);
567     GlobalFree(hGlobal);
568 
569     hwndOwner = GetWindow(hWnd, GW_OWNER);
570     if (hwndOwner)
571         SendMessageW(hwndOwner, WM_IME_NOTIFY, IMN_SOFTKBDDESTROYED, 0);
572 }
573 
574 static void
T1_InvertButton(_In_ HWND hWnd,_In_ HDC hDC,_In_ const T1WINDOW * pT1,_In_ UINT iPressed)575 T1_InvertButton(
576     _In_ HWND hWnd,
577     _In_ HDC hDC,
578     _In_ const T1WINDOW *pT1,
579     _In_ UINT iPressed)
580 {
581     INT cxWidth = pT1->cxDefWidth, cyHeight = pT1->cyDefHeight;
582     HDC hChoiceDC;
583 
584     if (iPressed >= T1K_NONE)
585         return;
586 
587     if (hDC)
588         hChoiceDC = hDC;
589     else
590         hChoiceDC = GetDC(hWnd);
591 
592     if (iPressed >= T1K_BACKSPACE)
593     {
594         switch (iPressed)
595         {
596             case T1K_BACKSPACE:
597                 cxWidth = pT1->cxWidth47;
598                 break;
599             case T1K_TAB:
600                 cxWidth = pT1->cxWidth48;
601                 break;
602             case T1K_ENTER:
603                 pT1 = pT1;
604                 cxWidth = pT1->cxWidth50;
605                 cyHeight = pT1->cyHeight50;
606                 break;
607             case T1K_ESCAPE:
608                 cxWidth = pT1->cxWidth57;
609                 break;
610             case T1K_SPACE:
611                 cxWidth = pT1->cxWidth58;
612                 break;
613             default:
614                 cxWidth = 0;
615                 MessageBeep(0xFFFFFFFF);
616                 break;
617         }
618     }
619 
620     if (cxWidth > 0)
621     {
622         PatBlt(hChoiceDC,
623                T1_KEYPOS(iPressed).x - 1, T1_KEYPOS(iPressed).y - 1,
624                cxWidth + 2, cyHeight + 2,
625                DSTINVERT);
626     }
627 
628     if (!hDC)
629         ReleaseDC(hWnd, hChoiceDC);
630 }
631 
632 static void
T1_OnDraw(_In_ HDC hDC,_In_ HWND hWnd)633 T1_OnDraw(
634     _In_ HDC hDC,
635     _In_ HWND hWnd)
636 {
637     HGLOBAL hGlobal;
638     PT1WINDOW pT1;
639     HDC hMemDC;
640     RECT rc;
641 
642     hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
643     pT1 = (PT1WINDOW)GlobalLock(hGlobal);
644     if (!hGlobal || !pT1)
645         return;
646 
647     hMemDC = CreateCompatibleDC(hDC);
648     SelectObject(hMemDC, pT1->hbmKeyboard);
649     GetClientRect(hWnd, &rc);
650     BitBlt(hDC, 0, 0, rc.right - rc.left, rc.bottom - rc.top, hMemDC, 0, 0, SRCCOPY);
651     DeleteDC(hMemDC);
652 
653     if (pT1->PressedKey < T1K_NONE)
654         T1_InvertButton(hWnd, hDC, pT1, pT1->PressedKey);
655 
656     GlobalUnlock(hGlobal);
657 }
658 
659 static UINT
T1_HitTest(_In_ const T1WINDOW * pT1,_In_ const POINT * ppt)660 T1_HitTest(
661     _In_ const T1WINDOW *pT1,
662     _In_ const POINT *ppt)
663 {
664     INT iKey;
665     for (iKey = 0; iKey < T1K_BACKSPACE; ++iKey)
666     {
667         const POINT *pptKey = &T1_KEYPOS(iKey);
668         if (Imm32PtInRect(ppt, pptKey->x, pptKey->y, pT1->cxDefWidth, pT1->cyDefHeight))
669             return iKey;
670     }
671 
672     if (Imm32PtInRect(ppt, T1_KEYPOS(T1K_BACKSPACE).x, T1_KEYPOS(T1K_BACKSPACE).y, pT1->cxWidth47, pT1->cyDefHeight))
673         return T1K_BACKSPACE;
674 
675     if (Imm32PtInRect(ppt, T1_KEYPOS(T1K_TAB).x, T1_KEYPOS(T1K_TAB).y, pT1->cxWidth48, pT1->cyDefHeight))
676         return T1K_TAB;
677 
678     if (Imm32PtInRect(ppt, T1_KEYPOS(T1K_CAPS).x, T1_KEYPOS(T1K_CAPS).y, pT1->cxWidth49, pT1->cyDefHeight))
679         return T1K_CAPS;
680 
681     if (Imm32PtInRect(ppt, T1_KEYPOS(T1K_ENTER).x, T1_KEYPOS(T1K_ENTER).y, pT1->cxWidth50, pT1->cyHeight50))
682         return T1K_ENTER;
683 
684     if (Imm32PtInRect(ppt, T1_KEYPOS(T1K_L_SHIFT).x, T1_KEYPOS(T1K_L_SHIFT).y, pT1->cxWidth51or52, pT1->cyDefHeight) ||
685         Imm32PtInRect(ppt, T1_KEYPOS(T1K_R_SHIFT).x, T1_KEYPOS(T1K_R_SHIFT).y, pT1->cxWidth51or52, pT1->cyDefHeight))
686     {
687         return T1K_L_SHIFT;
688     }
689 
690     if (Imm32PtInRect(ppt, T1_KEYPOS(T1K_L_CTRL).x, T1_KEYPOS(T1K_L_CTRL).y, pT1->cxWidth53or54, pT1->cyDefHeight) ||
691         Imm32PtInRect(ppt, T1_KEYPOS(T1K_R_CTRL).x, T1_KEYPOS(T1K_R_CTRL).y, pT1->cxWidth53or54, pT1->cyDefHeight))
692     {
693         return T1K_L_CTRL;
694     }
695 
696     if (Imm32PtInRect(ppt, T1_KEYPOS(T1K_L_ALT).x, T1_KEYPOS(T1K_L_ALT).y, pT1->cxWidth55or56, pT1->cyDefHeight) ||
697         Imm32PtInRect(ppt, T1_KEYPOS(T1K_R_ALT).x, T1_KEYPOS(T1K_R_ALT).y, pT1->cxWidth55or56, pT1->cyDefHeight))
698     {
699         return T1K_L_ALT;
700     }
701 
702     if (Imm32PtInRect(ppt, T1_KEYPOS(T1K_ESCAPE).x, T1_KEYPOS(T1K_ESCAPE).y, pT1->cxWidth57, pT1->cyDefHeight))
703         return T1K_ESCAPE;
704 
705     if (Imm32PtInRect(ppt, T1_KEYPOS(T1K_SPACE).x, T1_KEYPOS(T1K_SPACE).y, pT1->cxWidth58, pT1->cyDefHeight))
706         return T1K_SPACE;
707 
708     return T1K_NONE;
709 }
710 
711 static BOOL
T1_IsValidButton(_In_ UINT iKey,_In_ const T1WINDOW * pT1)712 T1_IsValidButton(
713     _In_ UINT iKey,
714     _In_ const T1WINDOW *pT1)
715 {
716     if (iKey < T1K_BACKSPACE)
717         return !!pT1->chKeyChar[iKey];
718     return iKey <= T1K_TAB || iKey == T1K_ENTER || (T1K_ESCAPE <= iKey && iKey <= T1K_SPACE);
719 }
720 
721 /**
722  * NOTE: The window that has WS_DISABLED style doesn't receive some mouse messages.
723  * Use WM_SETCURSOR handling to detect mouse events.
724  */
725 static BOOL
T1_OnSetCursor(_In_ HWND hWnd,_In_ LPARAM lParam)726 T1_OnSetCursor(
727     _In_ HWND hWnd,
728     _In_ LPARAM lParam)
729 {
730     HGLOBAL hGlobal;
731     PT1WINDOW pT1;
732     HCURSOR hCursor;
733     UINT iPressed, iKey;
734     RECT rc, rcWork;
735 
736     hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
737     pT1 = (PT1WINDOW)GlobalLock(hGlobal);
738     if (!hGlobal || !pT1)
739         return FALSE;
740 
741     if (pT1->pt1.x != -1 && pT1->pt1.y != -1)
742     {
743         SetCursor(LoadCursorW(NULL, (LPCWSTR)IDC_SIZEALL));
744         GlobalUnlock(hGlobal);
745         return TRUE;
746     }
747 
748     GetCursorPos(&pT1->pt0);
749     ScreenToClient(hWnd, &pT1->pt0);
750 
751     iKey = T1_HitTest(pT1, &pT1->pt0);
752     if (iKey >= T1K_NONE)
753         hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_SIZEALL);
754     else
755         hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_HAND);
756     SetCursor(hCursor);
757 
758     if (HIWORD(lParam) == WM_LBUTTONDOWN)
759     {
760         SetCapture(hWnd);
761 
762         iPressed = pT1->PressedKey;
763         if (iPressed < T1K_NONE)
764         {
765             UINT iVK = gT1K2VK[iPressed];
766             keybd_event(iVK, guScanCode[iVK], KEYEVENTF_KEYUP, 0);
767             T1_InvertButton(hWnd, NULL, pT1, pT1->PressedKey);
768             pT1->PressedKey = T1K_NONE;
769         }
770 
771         if (iKey >= T1K_NONE)
772         {
773             Imm32GetAllMonitorSize(&rcWork);
774             GetCursorPos(&pT1->pt0);
775             GetWindowRect(hWnd, &rc);
776             pT1->pt1.x = pT1->pt0.x - rc.left;
777             pT1->pt1.y = pT1->pt0.y - rc.top;
778             T1_DrawDragBorder(hWnd, &pT1->pt0, &pT1->pt1);
779         }
780         else if (T1_IsValidButton(iKey, pT1))
781         {
782             UINT iVK = gT1K2VK[iKey];
783             keybd_event(iVK, guScanCode[iVK], 0, 0);
784             pT1->PressedKey = iKey;
785             T1_InvertButton(hWnd, 0, pT1, iKey);
786         }
787         else
788         {
789             MessageBeep(0xFFFFFFFF);
790         }
791     }
792 
793     return TRUE;
794 }
795 
796 static BOOL
T1_OnMouseMove(_In_ HWND hWnd)797 T1_OnMouseMove(
798     _In_ HWND hWnd)
799 {
800     BOOL ret = FALSE;
801     HGLOBAL hGlobal;
802     PT1WINDOW pT1;
803 
804     hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
805     pT1 = (PT1WINDOW)GlobalLock(hGlobal);
806     if (!hGlobal || !pT1)
807         return FALSE;
808 
809     if (pT1->pt1.x != -1 && pT1->pt1.y != -1)
810     {
811         T1_DrawDragBorder(hWnd, &pT1->pt0, &pT1->pt1);
812         GetCursorPos(&pT1->pt0);
813         T1_DrawDragBorder(hWnd, &pT1->pt0, &pT1->pt1);
814         ret = TRUE;
815     }
816 
817     GlobalUnlock(hGlobal);
818     return ret;
819 }
820 
821 static BOOL
T1_OnButtonUp(_In_ HWND hWnd)822 T1_OnButtonUp(
823     _In_ HWND hWnd)
824 {
825     BOOL ret = FALSE;
826     HGLOBAL hGlobal;
827     PT1WINDOW pT1;
828     INT x, y, iPressed;
829     HWND hwndOwner, hwndCapture = GetCapture();
830     HIMC hIMC;
831     LPINPUTCONTEXT pIC;
832 
833     if (hwndCapture == hWnd)
834         ReleaseCapture();
835 
836     hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
837     pT1 = (PT1WINDOW)GlobalLock(hGlobal);
838     if (!hGlobal || !pT1)
839         return FALSE;
840 
841     iPressed = pT1->PressedKey;
842     if (iPressed >= T1K_NONE)
843     {
844         if (pT1->pt1.x != -1 && pT1->pt1.y != -1)
845         {
846             T1_DrawDragBorder(hWnd, &pT1->pt0, &pT1->pt1);
847             x = pT1->pt0.x - pT1->pt1.x;
848             y = pT1->pt0.y - pT1->pt1.y;
849             SetWindowPos(hWnd, NULL, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
850             pT1->pt1.x = pT1->pt1.y = -1;
851             pT1->PressedKey = T1K_NONE;
852             ret = TRUE;
853 
854             hwndOwner = GetWindow(hWnd, GW_OWNER);
855             hIMC = (HIMC)GetWindowLongPtrW(hwndOwner, 0);
856             if (hIMC)
857             {
858                 pIC = ImmLockIMC(hIMC);
859                 if (pIC)
860                 {
861                     pIC->fdwInit |= INIT_SOFTKBDPOS;
862                     pIC->ptSoftKbdPos.x = x;
863                     pIC->ptSoftKbdPos.y = y;
864                     ImmUnlockIMC(hIMC);
865                 }
866             }
867         }
868     }
869     else
870     {
871         UINT iVK = gT1K2VK[iPressed];
872         keybd_event(iVK, guScanCode[iVK], KEYEVENTF_KEYUP, 0);
873 
874         T1_InvertButton(hWnd, 0, pT1, pT1->PressedKey);
875         pT1->PressedKey = T1K_NONE;
876         ret = TRUE;
877     }
878 
879     GlobalUnlock(hGlobal);
880     return ret;
881 }
882 
883 static LRESULT
T1_SetData(_In_ HWND hWnd,_In_ const SOFTKBDDATA * pData)884 T1_SetData(
885     _In_ HWND hWnd,
886     _In_ const SOFTKBDDATA *pData)
887 {
888     HGLOBAL hGlobal;
889     PT1WINDOW pT1;
890     HDC hDC, hMemDC;
891     HFONT hFont;
892     HGDIOBJ hFontOld, hbmOld;
893     RECT rc;
894     INT iKey;
895 
896     hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
897     pT1 = (PT1WINDOW)GlobalLock(hGlobal);
898     if (!hGlobal || !pT1)
899         return 1;
900 
901     hDC = GetDC(hWnd);
902     hMemDC = CreateCompatibleDC(hDC);
903     ReleaseDC(hWnd, hDC);
904 
905     hbmOld = SelectObject(hMemDC, pT1->hbmKeyboard);
906 #if 0 /* The default text color is black */
907     SetTextColor(hMemDC, RGB(0, 0, 0));
908 #endif
909     SetBkColor(hMemDC, RGB(192, 192, 192));
910 
911     if (pT1->CharSet == DEFAULT_CHARSET)
912     {
913         hFont = CreateFontIndirectW(&g_T1LogFont);
914     }
915     else
916     {
917         LOGFONTW lf = g_T1LogFont;
918         lf.lfCharSet = (BYTE)pT1->CharSet;
919         hFont = CreateFontIndirectW(&lf);
920     }
921     hFontOld = SelectObject(hMemDC, hFont);
922 
923     for (iKey = 0; iKey < T1K_BACKSPACE; ++iKey)
924     {
925         INT x0 = T1_KEYPOS(iKey).x, y0 = T1_KEYPOS(iKey).y;
926         INT x = x0 + 6, y = y0 + 8;
927         WCHAR wch = pT1->chKeyChar[iKey] = pData->wCode[0][gT1K2VK[iKey]];
928         SetRect(&rc, x, y, x0 + pT1->cxDefWidth, y0 + pT1->cyDefHeight);
929         ExtTextOutW(hMemDC, x, y, ETO_OPAQUE, &rc, &wch, wch != 0, NULL);
930     }
931 
932     DeleteObject(SelectObject(hMemDC, hFontOld));
933     SelectObject(hMemDC, hbmOld);
934     DeleteDC(hMemDC);
935     GlobalUnlock(hGlobal);
936     return 0;
937 }
938 
939 static LRESULT
T1_OnImeControl(_In_ HWND hWnd,_Inout_ WPARAM wParam,_Inout_ LPARAM lParam)940 T1_OnImeControl(
941     _In_ HWND hWnd,
942     _Inout_ WPARAM wParam,
943     _Inout_ LPARAM lParam)
944 {
945     LRESULT ret = 1;
946     PT1WINDOW pT1;
947     HGLOBAL hGlobal;
948 
949     switch (wParam)
950     {
951         case IMC_GETSOFTKBDFONT:
952         {
953             TRACE("IMC_GETSOFTKBDFONT: %p\n", lParam);
954             hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
955             pT1 = (PT1WINDOW)GlobalLock(hGlobal);
956             if (hGlobal && pT1)
957             {
958                 LPLOGFONTW plf = (LPLOGFONTW)lParam;
959                 DWORD CharSet = pT1->CharSet;
960                 GlobalUnlock(hGlobal);
961 
962                 *plf = g_T1LogFont;
963                 if (CharSet != DEFAULT_CHARSET)
964                     plf->lfCharSet = (BYTE)CharSet;
965 
966                 ret = 0;
967             }
968             break;
969         }
970         case IMC_SETSOFTKBDFONT:
971         {
972             const LOGFONTW *plf = (LPLOGFONTW)lParam;
973             TRACE("IMC_SETSOFTKBDFONT: %p\n", lParam);
974             if (g_T1LogFont.lfCharSet == plf->lfCharSet)
975                 return 0;
976 
977             hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
978             pT1 = (PT1WINDOW)GlobalLock(hGlobal);
979             if (hGlobal && pT1)
980             {
981                 pT1->CharSet = plf->lfCharSet;
982                 GlobalUnlock(hGlobal);
983                 return 0;
984             }
985 
986             break;
987         }
988         case IMC_GETSOFTKBDPOS:
989         {
990             RECT rc;
991             TRACE("IMC_GETSOFTKBDPOS\n");
992             GetWindowRect(hWnd, &rc);
993             return MAKELRESULT(rc.left, rc.top);
994         }
995         case IMC_SETSOFTKBDPOS:
996         {
997             POINT pt;
998             HWND hwndParent;
999 
1000             POINTSTOPOINT(pt, lParam);
1001             TRACE("IMC_SETSOFTKBDPOS(%ld, %ld)\n", pt.x, pt.y);
1002 
1003             SetWindowPos(hWnd, NULL, pt.x, pt.y, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
1004 
1005             hwndParent = GetParent(hWnd);
1006             if (hwndParent)
1007             {
1008                 HIMC hIMC = (HIMC)GetWindowLongPtrW(hwndParent, 0);
1009                 if (hIMC)
1010                 {
1011                     LPINPUTCONTEXT pIC = ImmLockIMC(hIMC);
1012                     if (pIC)
1013                     {
1014                         pIC->ptSoftKbdPos.x = pt.x;
1015                         pIC->ptSoftKbdPos.y = pt.y;
1016                         ImmUnlockIMC(hIMC);
1017                         return 0;
1018                     }
1019                 }
1020             }
1021             break;
1022         }
1023         case IMC_GETSOFTKBDSUBTYPE:
1024         case IMC_SETSOFTKBDSUBTYPE:
1025         {
1026             TRACE("IMC_GETSOFTKBDSUBTYPE/IMC_SETSOFTKBDSUBTYPE\n");
1027             hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
1028             pT1 = (PT1WINDOW)GlobalLock(hGlobal);
1029             if (!hGlobal || !pT1)
1030                 return -1;
1031 
1032             ret = pT1->KeyboardSubType;
1033 
1034             if (wParam == IMC_SETSOFTKBDSUBTYPE)
1035                 pT1->KeyboardSubType = lParam;
1036 
1037             GlobalUnlock(hGlobal);
1038             break;
1039         }
1040         case IMC_SETSOFTKBDDATA:
1041         {
1042             TRACE("IMC_SETSOFTKBDDATA: %p\n", lParam);
1043             ret = T1_SetData(hWnd, (SOFTKBDDATA*)lParam);
1044             if (!ret)
1045             {
1046                 InvalidateRect(hWnd, NULL, FALSE);
1047                 PostMessageW(hWnd, WM_PAINT, 0, 0);
1048             }
1049             break;
1050         }
1051     }
1052 
1053     return ret;
1054 }
1055 
1056 static LRESULT CALLBACK
T1_WindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)1057 T1_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1058 {
1059     switch (uMsg)
1060     {
1061         case WM_CREATE:
1062         {
1063             return T1_OnCreate(hWnd);
1064         }
1065         case WM_DESTROY:
1066         {
1067             T1_OnDestroy(hWnd);
1068             break;
1069         }
1070         case WM_SETCURSOR:
1071         {
1072             if (T1_OnSetCursor(hWnd, lParam))
1073                 break;
1074             return DefWindowProcW(hWnd, uMsg, wParam, lParam);
1075         }
1076         case WM_MOUSEMOVE:
1077         {
1078             if (T1_OnMouseMove(hWnd))
1079                 break;
1080             return DefWindowProcW(hWnd, uMsg, wParam, lParam);
1081         }
1082         case WM_PAINT:
1083         {
1084             PAINTSTRUCT ps;
1085             HDC hDC = BeginPaint(hWnd, &ps);
1086             T1_OnDraw(hDC, hWnd);
1087             EndPaint(hWnd, &ps);
1088             break;
1089         }
1090         case WM_SHOWWINDOW:
1091         {
1092             if (!lParam && wParam != SW_SHOWNORMAL)
1093                 T1_OnButtonUp(hWnd);
1094             return DefWindowProcW(hWnd, uMsg, wParam, lParam);
1095         }
1096         case WM_MOUSEACTIVATE:
1097         {
1098             return MA_NOACTIVATE;
1099         }
1100         case WM_LBUTTONUP:
1101         {
1102             if (T1_OnButtonUp(hWnd))
1103                 break;
1104             return DefWindowProcW(hWnd, uMsg, wParam, lParam);
1105         }
1106         case WM_IME_CONTROL:
1107         {
1108             return T1_OnImeControl(hWnd, wParam, lParam);
1109         }
1110         default:
1111         {
1112             return DefWindowProcW(hWnd, uMsg, wParam, lParam);
1113         }
1114     }
1115 
1116     return 0;
1117 }
1118 
1119 /*****************************************************************************
1120  * IME Soft Keyboard Type C1
1121  */
1122 
1123 #define C1_CLASSNAMEW L"SoftKBDClsC1"
1124 
1125 #define C1K_MAX 56
1126 
1127 #undef DEFINE_C1K
1128 #define DEFINE_C1K(c1k_code, virtual_key_code, c1k_code_name, virtual_key_name, is_special) \
1129     c1k_code_name = c1k_code,
1130 
1131 /* Define C1 internal codes (C1K_...) */
1132 typedef enum C1KEY
1133 {
1134 #include "c1keys.h"
1135 } C1KEY;
1136 
1137 #undef DEFINE_C1K
1138 #define DEFINE_C1K(c1k_code, virtual_key_code, c1k_code_name, virtual_key_name, is_special) \
1139     virtual_key_code,
1140 
1141 /* Mapping: C1K --> Virtual Key */
1142 const BYTE gC1K2VK[C1K_MAX] =
1143 {
1144 #include "c1keys.h"
1145 };
1146 
1147 typedef struct C1WINDOW
1148 {
1149     WCHAR Data[2][47];
1150     DWORD dwFlags;
1151     HBITMAP hbmKeyboard;
1152     LPARAM SubType;
1153     INT iPressedKey;
1154     POINT pt1, pt2;
1155     DWORD CharSet;
1156 } C1WINDOW, *PC1WINDOW;
1157 
1158 /* The flags for C1WINDOW.dwFlags */
1159 #define FLAG_SHIFT_PRESSED 1
1160 #define FLAG_DRAGGING 2
1161 #define FLAG_PRESSED 4
1162 
1163 static BOOL gbC1ButtonInit = FALSE;
1164 static POINT gptC1ButtonPos[C1K_MAX];
1165 
C1_InitButtonPos(void)1166 static void C1_InitButtonPos(void)
1167 {
1168     LONG x = 0, y = 0;
1169     INT iKey;
1170 
1171     /* 1st row */
1172     for (iKey = C1K_OEM_3; iKey < C1K_Q; ++iKey)
1173     {
1174         gptC1ButtonPos[iKey].x = x;
1175         gptC1ButtonPos[iKey].y = y;
1176         x += 24;
1177     }
1178     gptC1ButtonPos[C1K_BACKSPACE].x = x;
1179     gptC1ButtonPos[C1K_BACKSPACE].y = y;
1180 
1181     /* 2nd row */
1182     y = 28;
1183     gptC1ButtonPos[C1K_TAB].x = 0;
1184     gptC1ButtonPos[C1K_TAB].y = y;
1185     x = 36;
1186     for (; iKey < C1K_A; ++iKey)
1187     {
1188         gptC1ButtonPos[iKey].x = x;
1189         gptC1ButtonPos[iKey].y = y;
1190         x += 24;
1191     }
1192 
1193     /* 3rd row */
1194     y = 56;
1195     gptC1ButtonPos[C1K_CAPS].x = 0;
1196     gptC1ButtonPos[C1K_CAPS].y = y;
1197     x = 42;
1198     for (; iKey < C1K_Z; ++iKey)
1199     {
1200         gptC1ButtonPos[iKey].x = x;
1201         gptC1ButtonPos[iKey].y = y;
1202         x += 24;
1203     }
1204     gptC1ButtonPos[C1K_ENTER].x = x;
1205     gptC1ButtonPos[C1K_ENTER].y = y;
1206 
1207     /* 4th row */
1208     y = 84;
1209     gptC1ButtonPos[C1K_SHIFT].x = 0;
1210     gptC1ButtonPos[C1K_SHIFT].y = y;
1211     x = 60;
1212     for (; iKey < C1K_BACKSPACE; ++iKey)
1213     {
1214         gptC1ButtonPos[iKey].x = x;
1215         gptC1ButtonPos[iKey].y = y;
1216         x += 24;
1217     }
1218 
1219     /* 5th row */
1220     y = 112;
1221     gptC1ButtonPos[C1K_INSERT].x = 0;
1222     gptC1ButtonPos[C1K_INSERT].y = y;
1223     gptC1ButtonPos[C1K_DELETE].x = 58;
1224     gptC1ButtonPos[C1K_DELETE].y = y;
1225     gptC1ButtonPos[C1K_SPACE].x = 96;
1226     gptC1ButtonPos[C1K_SPACE].y = y;
1227     gptC1ButtonPos[C1K_ESCAPE].x = 310;
1228     gptC1ButtonPos[C1K_ESCAPE].y = y;
1229 }
1230 
1231 static void
C1_DrawConvexRect(_In_ HDC hDC,_In_ INT x,_In_ INT y,_In_ INT width,_In_ INT height)1232 C1_DrawConvexRect(
1233     _In_ HDC hDC,
1234     _In_ INT x,
1235     _In_ INT y,
1236     _In_ INT width,
1237     _In_ INT height)
1238 {
1239     HGDIOBJ hLtGrayBrush = GetStockObject(LTGRAY_BRUSH);
1240     HGDIOBJ hBlackPen = GetStockObject(BLACK_PEN);
1241     HGDIOBJ hWhiteBrush = GetStockObject(WHITE_BRUSH);
1242     HGDIOBJ hGrayBrush = GetStockObject(GRAY_BRUSH);
1243     INT y2 = y + height - 1;
1244 
1245     /* Draw face */
1246     SelectObject(hDC, hLtGrayBrush);
1247     SelectObject(hDC, hBlackPen);
1248     Rectangle(hDC, x, y, x + width, y + height);
1249 
1250     /* Draw light edge */
1251     SelectObject(hDC, hWhiteBrush);
1252     PatBlt(hDC, x, y2, 2, 1 - height, PATCOPY);
1253     PatBlt(hDC, x, y, width - 1, 2, PATCOPY);
1254 
1255     /* Draw dark edge */
1256     SelectObject(hDC, hGrayBrush);
1257     PatBlt(hDC, x + 1, y2, width - 2, -1, PATCOPY);
1258     PatBlt(hDC, x + width - 1, y2, -1, 2 - height, PATCOPY);
1259 }
1260 
1261 static void
C1_InvertButton(_In_ HDC hDC,_In_ INT iKey)1262 C1_InvertButton(
1263     _In_ HDC hDC,
1264     _In_ INT iKey)
1265 {
1266     INT width = 24, height = 28;
1267 
1268     if (iKey < 0)
1269         return;
1270 
1271     switch (iKey)
1272     {
1273         case C1K_BACKSPACE: case C1K_TAB:
1274             width = 36;
1275             break;
1276         case C1K_CAPS: case C1K_ENTER:
1277             width = 42;
1278             break;
1279         case C1K_SHIFT:
1280             width = 60;
1281             break;
1282         case C1K_INSERT: case C1K_DELETE: case C1K_ESCAPE:
1283             width = 38;
1284             height = 24;
1285             break;
1286         case C1K_SPACE:
1287             width = 172;
1288             height = 24;
1289             break;
1290         default:
1291             break;
1292     }
1293 
1294     BitBlt(hDC, gptC1ButtonPos[iKey].x, gptC1ButtonPos[iKey].y, width, height,
1295            hDC, gptC1ButtonPos[iKey].x, gptC1ButtonPos[iKey].y, DSTINVERT);
1296 }
1297 
1298 static void
C1_DrawLabel(_In_ HDC hDC,_In_ INT nBitmapID)1299 C1_DrawLabel(
1300     _In_ HDC hDC,
1301     _In_ INT nBitmapID)
1302 {
1303     HBITMAP hBitmap;
1304     HGDIOBJ hbmOld;
1305     HDC hMemDC;
1306     INT iKey;
1307 
1308     hBitmap = LoadBitmapW(ghImm32Inst, MAKEINTRESOURCEW(nBitmapID));
1309     hMemDC = CreateCompatibleDC(hDC);
1310     hbmOld = SelectObject(hMemDC, hBitmap);
1311     for (iKey = C1K_OEM_3; iKey < C1K_BACKSPACE; ++iKey)
1312     {
1313         BitBlt(hDC, gptC1ButtonPos[iKey].x + 2, gptC1ButtonPos[iKey].y + 2, 8, 8,
1314                hMemDC, iKey * 8, 0, SRCCOPY);
1315     }
1316     DeleteObject(SelectObject(hMemDC, hbmOld));
1317     DeleteDC(hMemDC);
1318 }
1319 
1320 static void
C1_InitBitmap(_In_ HDC hDC,_In_ INT x,_In_ INT y,_In_ INT width,_In_ INT height)1321 C1_InitBitmap(
1322     _In_ HDC hDC,
1323     _In_ INT x,
1324     _In_ INT y,
1325     _In_ INT width,
1326     _In_ INT height)
1327 {
1328     HGDIOBJ hLtGrayBrush = GetStockObject(LTGRAY_BRUSH);
1329     HGDIOBJ hNullPen = GetStockObject(NULL_PEN);
1330     INT iKey;
1331 
1332     /* Draw keyboard frame */
1333     SelectObject(hDC, hLtGrayBrush);
1334     SelectObject(hDC, hNullPen);
1335     Rectangle(hDC, x, y, width + 1, height + 1);
1336 
1337     for (iKey = C1K_OEM_3; iKey < C1K_BACKSPACE; ++iKey)
1338     {
1339         C1_DrawConvexRect(hDC, gptC1ButtonPos[iKey].x, gptC1ButtonPos[iKey].y, 24, 28);
1340     }
1341 
1342     C1_DrawLabel(hDC, IDB_C1_CHARS);
1343 
1344     C1_DrawConvexRect(hDC, gptC1ButtonPos[C1K_BACKSPACE].x, gptC1ButtonPos[C1K_BACKSPACE].y, 36, 28);
1345     Imm32DrawBitmap(hDC, gptC1ButtonPos[C1K_BACKSPACE].x + 2, gptC1ButtonPos[C1K_BACKSPACE].y + 2, 32, 24, IDB_C1_BACKSPACE);
1346 
1347     C1_DrawConvexRect(hDC, gptC1ButtonPos[C1K_TAB].x, gptC1ButtonPos[C1K_TAB].y, 36, 28);
1348     Imm32DrawBitmap(hDC, gptC1ButtonPos[C1K_TAB].x + 2, gptC1ButtonPos[C1K_TAB].y + 2, 32, 24, IDB_C1_TAB);
1349 
1350     C1_DrawConvexRect(hDC, gptC1ButtonPos[C1K_CAPS].x, gptC1ButtonPos[C1K_CAPS].y, 42, 28);
1351     Imm32DrawBitmap(hDC, gptC1ButtonPos[C1K_CAPS].x + 2, gptC1ButtonPos[C1K_CAPS].y + 2, 38, 24, IDB_C1_CAPS);
1352 
1353     C1_DrawConvexRect(hDC, gptC1ButtonPos[C1K_ENTER].x, gptC1ButtonPos[C1K_ENTER].y, 42, 28);
1354     Imm32DrawBitmap(hDC, gptC1ButtonPos[C1K_ENTER].x + 2, gptC1ButtonPos[C1K_ENTER].y + 2, 38, 24, IDB_C1_ENTER);
1355 
1356     C1_DrawConvexRect(hDC, gptC1ButtonPos[C1K_SHIFT].x, gptC1ButtonPos[C1K_SHIFT].y, 60, 28);
1357     Imm32DrawBitmap(hDC, gptC1ButtonPos[C1K_SHIFT].x + 2, gptC1ButtonPos[C1K_SHIFT].y + 2, 56, 24, IDB_C1_SHIFT);
1358 
1359     C1_DrawConvexRect(hDC, gptC1ButtonPos[C1K_INSERT].x, gptC1ButtonPos[C1K_INSERT].y, 38, 24);
1360     Imm32DrawBitmap(hDC, gptC1ButtonPos[C1K_INSERT].x + 2, gptC1ButtonPos[C1K_INSERT].y + 2, 34, 20, IDB_C1_INS);
1361 
1362     C1_DrawConvexRect(hDC, gptC1ButtonPos[C1K_DELETE].x, gptC1ButtonPos[C1K_DELETE].y, 38, 24);
1363     Imm32DrawBitmap(hDC, gptC1ButtonPos[C1K_DELETE].x + 2, gptC1ButtonPos[C1K_DELETE].y + 2, 34, 20, IDB_C1_DEL);
1364 
1365     C1_DrawConvexRect(hDC, gptC1ButtonPos[C1K_SPACE].x, gptC1ButtonPos[C1K_SPACE].y, 172, 24);
1366 
1367     C1_DrawConvexRect(hDC, gptC1ButtonPos[C1K_ESCAPE].x, gptC1ButtonPos[C1K_ESCAPE].y , 38, 24);
1368     Imm32DrawBitmap(hDC, gptC1ButtonPos[C1K_ESCAPE].x + 2, gptC1ButtonPos[C1K_ESCAPE].y + 2, 34, 20, IDB_C1_ESCAPE);
1369 }
1370 
1371 static INT
C1_OnCreate(_In_ HWND hWnd)1372 C1_OnCreate(
1373     _In_ HWND hWnd)
1374 {
1375     HGLOBAL hGlobal;
1376     PC1WINDOW pC1;
1377     HDC hDC, hMemDC;
1378     RECT rc;
1379     HGDIOBJ hbmOld;
1380     HBITMAP hbmKeyboard;
1381 
1382     hGlobal = GlobalAlloc(GHND, sizeof(C1WINDOW));
1383     if (!hGlobal)
1384         return -1;
1385 
1386     pC1 = (PC1WINDOW)GlobalLock(hGlobal);
1387     if (!pC1)
1388     {
1389         GlobalFree(hGlobal);
1390         return -1;
1391     }
1392     SetWindowLongPtrW(hWnd, 0, (LONG_PTR)hGlobal);
1393 
1394     if (!gbC1ButtonInit)
1395     {
1396         C1_InitButtonPos();
1397         gbC1ButtonInit = TRUE;
1398     }
1399 
1400     pC1->iPressedKey = -1;
1401     pC1->CharSet = GB2312_CHARSET;
1402 
1403     GetClientRect(hWnd, &rc);
1404 
1405     hDC = GetDC(hWnd);
1406     hMemDC = CreateCompatibleDC(hDC);
1407     hbmKeyboard = CreateCompatibleBitmap(hDC, rc.right - rc.left, rc.bottom - rc.top);
1408     ReleaseDC(hWnd, hDC);
1409 
1410     hbmOld = SelectObject(hMemDC, hbmKeyboard);
1411     C1_InitBitmap(hMemDC, rc.left, rc.top, rc.right, rc.bottom);
1412     SelectObject(hMemDC, hbmOld);
1413     pC1->hbmKeyboard = hbmKeyboard;
1414     DeleteDC(hMemDC);
1415 
1416     GlobalUnlock(hGlobal);
1417     return 0;
1418 }
1419 
1420 static void
C1_OnDraw(_In_ HDC hDC,_In_ HWND hWnd)1421 C1_OnDraw(
1422     _In_ HDC hDC,
1423     _In_ HWND hWnd)
1424 {
1425     HGLOBAL hGlobal;
1426     PC1WINDOW pC1;
1427     HDC hMemDC;
1428     RECT rc;
1429     HGDIOBJ hbmOld;
1430 
1431     hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
1432     pC1 = (PC1WINDOW)GlobalLock(hGlobal);
1433     if (!hGlobal || !pC1)
1434         return;
1435 
1436     GetClientRect(hWnd, &rc);
1437 
1438     hMemDC = CreateCompatibleDC(hDC);
1439     hbmOld = SelectObject(hMemDC, pC1->hbmKeyboard);
1440     BitBlt(hDC, 0, 0, rc.right - rc.left, rc.bottom - rc.top, hMemDC, 0, 0, SRCCOPY);
1441     SelectObject(hMemDC, hbmOld);
1442     DeleteDC(hMemDC);
1443 
1444     GlobalUnlock(hGlobal);
1445 }
1446 
1447 static BOOL
C1_SetData(_In_ HWND hWnd,_In_ const SOFTKBDDATA * pData)1448 C1_SetData(
1449     _In_ HWND hWnd,
1450     _In_ const SOFTKBDDATA *pData)
1451 {
1452     HGLOBAL hGlobal;
1453     PC1WINDOW pC1;
1454     HDC hDC, hMemDC;
1455     INT iKey;
1456     BOOL bDisabled;
1457     HBITMAP hbmKeyboard;
1458     HGDIOBJ hbmOld, hFontOld;
1459     HFONT hFont;
1460     RECT rc;
1461     LOGFONTW lf;
1462 
1463     if (pData->uCount != 2)
1464         return 0;
1465 
1466     hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
1467     pC1 = (PC1WINDOW)GlobalLock(hGlobal);
1468     if (!hGlobal || !pC1)
1469         return FALSE;
1470 
1471     hDC = GetDC(hWnd);
1472     hMemDC = CreateCompatibleDC(hDC);
1473 
1474     hbmKeyboard = pC1->hbmKeyboard;
1475     hbmOld = SelectObject(hMemDC, hbmKeyboard);
1476 
1477     GetObjectW(GetStockObject(DEFAULT_GUI_FONT), sizeof(lf), &lf);
1478     lf.lfHeight = -12;
1479     if (pC1->CharSet != DEFAULT_CHARSET)
1480         lf.lfCharSet = (BYTE)pC1->CharSet;
1481 
1482     hFont = CreateFontIndirectW(&lf);
1483     hFontOld = SelectObject(hMemDC, hFont);
1484     for (iKey = C1K_OEM_3; iKey < C1K_BACKSPACE; ++iKey)
1485     {
1486         pC1->Data[1][iKey] = pData->wCode[0][(BYTE)gC1K2VK[iKey]];
1487         pC1->Data[0][iKey] = pData->wCode[1][(BYTE)gC1K2VK[iKey]];
1488     }
1489 
1490     SetBkColor(hMemDC, RGB(191, 191, 191));
1491     for (iKey = C1K_OEM_3; iKey < C1K_BACKSPACE; ++iKey)
1492     {
1493         /* Upper right */
1494         rc.right = gptC1ButtonPos[iKey].x + 24 - 2;
1495         rc.top = gptC1ButtonPos[iKey].y + 2;
1496         rc.left = rc.right - 14;
1497         rc.bottom = rc.top + 14;
1498         bDisabled = (pC1->Data[0][iKey] == 0);
1499         DrawTextW(hMemDC, &pC1->Data[0][iKey], !bDisabled, &rc,
1500                   DT_RIGHT | DT_TOP | DT_SINGLELINE);
1501 
1502         /* Lower left */
1503         rc.left = gptC1ButtonPos[iKey].x + 2;
1504         rc.bottom = gptC1ButtonPos[iKey].y + 28 - 2;
1505         rc.right = rc.left + 14;
1506         rc.top = rc.bottom - 14;
1507         bDisabled = (pC1->Data[1][iKey] == 0);
1508         DrawTextW(hMemDC, &pC1->Data[1][iKey], !bDisabled, &rc,
1509                   DT_LEFT | DT_BOTTOM | DT_SINGLELINE);
1510     }
1511 
1512     if (pC1->dwFlags & FLAG_SHIFT_PRESSED)
1513         C1_InvertButton(hMemDC, C1K_SHIFT);
1514 
1515     pC1->dwFlags = 0;
1516 
1517     SelectObject(hMemDC, hbmOld);
1518     DeleteObject(SelectObject(hMemDC, hFontOld));
1519 
1520     DeleteDC(hMemDC);
1521     ReleaseDC(hWnd, hDC);
1522 
1523     GlobalUnlock(hGlobal);
1524     return TRUE;
1525 }
1526 
1527 static void
C1_DrawDragBorder(_In_ HWND hWnd,_In_ LPPOINT ppt1,_Inout_ LPPOINT ppt2)1528 C1_DrawDragBorder(
1529     _In_ HWND hWnd,
1530     _In_ LPPOINT ppt1,
1531     _Inout_ LPPOINT ppt2)
1532 {
1533     HGDIOBJ hGrayBrush = GetStockObject(GRAY_BRUSH);
1534     INT x, y;
1535     RECT rc, rcWork;
1536     INT cxBorder = GetSystemMetrics(SM_CXBORDER), cyBorder = GetSystemMetrics(SM_CYBORDER);
1537     HDC hDisplayDC;
1538 
1539     Imm32GetAllMonitorSize(&rcWork);
1540     hDisplayDC = CreateDCW(L"DISPLAY", NULL, NULL, NULL);
1541 
1542     SelectObject(hDisplayDC, hGrayBrush);
1543     x = ppt1->x - ppt2->x;
1544     y = ppt1->y - ppt2->y;
1545     if (x < rcWork.left)
1546         x = rcWork.left;
1547     if (y < rcWork.top)
1548         y = rcWork.top;
1549 
1550     GetWindowRect(hWnd, &rc);
1551 
1552     if (rc.right - rc.left + x > rcWork.right)
1553         x = rc.left + rcWork.right - rc.right;
1554     if (y + rc.bottom - rc.top > rcWork.bottom)
1555         y = rc.top + rcWork.bottom - rc.bottom;
1556 
1557     ppt2->x = ppt1->x - x;
1558     ppt2->y = ppt1->y - y;
1559 
1560     PatBlt(hDisplayDC, x, y, rc.right - rc.left - cxBorder, cyBorder, PATINVERT);
1561     PatBlt(hDisplayDC, x, y + cyBorder, cxBorder, rc.bottom - rc.top - cyBorder, PATINVERT);
1562     PatBlt(hDisplayDC, x + cxBorder, y + rc.bottom - rc.top, rc.right - rc.left - cxBorder, -cyBorder, PATINVERT);
1563     PatBlt(hDisplayDC, x + rc.right - rc.left, y, -cxBorder, rc.bottom - rc.top - cyBorder, PATINVERT);
1564 
1565     DeleteDC(hDisplayDC);
1566 }
1567 
1568 static INT
C1_HitTest(_In_ const POINT * ppt)1569 C1_HitTest(
1570     _In_ const POINT *ppt)
1571 {
1572     INT iKey;
1573 
1574     for (iKey = C1K_OEM_3; iKey < C1K_BACKSPACE; ++iKey)
1575     {
1576         if (Imm32PtInRect(ppt, gptC1ButtonPos[iKey].x, gptC1ButtonPos[iKey].y, 24, 28))
1577             return iKey;
1578     }
1579 
1580     if (Imm32PtInRect(ppt, gptC1ButtonPos[C1K_BACKSPACE].x, gptC1ButtonPos[C1K_BACKSPACE].y, 36, 28))
1581         return C1K_BACKSPACE;
1582     if (Imm32PtInRect(ppt, gptC1ButtonPos[C1K_TAB].x, gptC1ButtonPos[C1K_TAB].y, 36, 28))
1583         return C1K_TAB;
1584     if (Imm32PtInRect(ppt, gptC1ButtonPos[C1K_CAPS].x, gptC1ButtonPos[C1K_CAPS].y, 42, 28))
1585         return C1K_CAPS;
1586     if (Imm32PtInRect(ppt, gptC1ButtonPos[C1K_ENTER].x, gptC1ButtonPos[C1K_ENTER].y, 42, 28))
1587         return C1K_ENTER;
1588     if (Imm32PtInRect(ppt, gptC1ButtonPos[C1K_SHIFT].x, gptC1ButtonPos[C1K_SHIFT].y, 60, 28))
1589         return C1K_SHIFT;
1590     if (Imm32PtInRect(ppt, gptC1ButtonPos[C1K_ESCAPE].x, gptC1ButtonPos[C1K_ESCAPE].y, 38, 24))
1591         return C1K_ESCAPE;
1592     if (Imm32PtInRect(ppt, gptC1ButtonPos[C1K_SPACE].x, gptC1ButtonPos[C1K_SPACE].y, 172, 24))
1593         return C1K_SPACE;
1594     if (Imm32PtInRect(ppt, gptC1ButtonPos[C1K_INSERT].x, gptC1ButtonPos[C1K_INSERT].y, 38, 24))
1595         return C1K_INSERT;
1596     if (Imm32PtInRect(ppt, gptC1ButtonPos[C1K_DELETE].x, gptC1ButtonPos[C1K_DELETE].y, 38, 24))
1597         return C1K_DELETE;
1598 
1599     return -1;
1600 }
1601 
1602 static void
C1_OnButtonDown(_In_ HWND hWnd,_Inout_ PC1WINDOW pC1)1603 C1_OnButtonDown(
1604     _In_ HWND hWnd,
1605     _Inout_ PC1WINDOW pC1)
1606 {
1607     INT iPressedKey;
1608     HDC hMemDC;
1609     WCHAR wch = 0xFF;
1610     HGDIOBJ hbmOld;
1611     HDC hDC;
1612 
1613     SetCapture(hWnd);
1614 
1615     iPressedKey = pC1->iPressedKey;
1616     if (iPressedKey == -1)
1617     {
1618         pC1->dwFlags |= FLAG_DRAGGING;
1619         C1_DrawDragBorder(hWnd, &pC1->pt1, &pC1->pt2);
1620         return;
1621     }
1622 
1623     if (iPressedKey < C1K_BACKSPACE)
1624     {
1625         wch = pC1->Data[!(pC1->dwFlags & 1)][iPressedKey];
1626         if (!wch)
1627         {
1628             MessageBeep(0xFFFFFFFF);
1629             pC1->iPressedKey = -1;
1630             return;
1631         }
1632     }
1633 
1634     if ((iPressedKey != C1K_SHIFT) || !(pC1->dwFlags & FLAG_SHIFT_PRESSED))
1635     {
1636         hDC = GetDC(hWnd);
1637         hMemDC = CreateCompatibleDC(hDC);
1638         hbmOld = SelectObject(hMemDC, pC1->hbmKeyboard);
1639         C1_InvertButton(hDC, pC1->iPressedKey);
1640         C1_InvertButton(hMemDC, pC1->iPressedKey);
1641         SelectObject(hMemDC, hbmOld);
1642         DeleteDC(hMemDC);
1643         ReleaseDC(hWnd, hDC);
1644     }
1645 
1646     pC1->dwFlags |= FLAG_PRESSED;
1647 }
1648 
1649 static BOOL
C1_OnSetCursor(_In_ HWND hWnd,_In_ LPARAM lParam)1650 C1_OnSetCursor(
1651     _In_ HWND hWnd,
1652     _In_ LPARAM lParam)
1653 {
1654     HGLOBAL hGlobal;
1655     PC1WINDOW pC1;
1656     HCURSOR hCursor;
1657     INT iKey;
1658     POINT pt1, pt2;
1659 
1660     hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
1661     pC1 = (PC1WINDOW)GlobalLock(hGlobal);
1662     if (!hGlobal || !pC1)
1663         return FALSE;
1664 
1665     if (pC1->dwFlags & FLAG_DRAGGING)
1666     {
1667         hCursor = LoadCursorW(0, (LPCWSTR)IDC_SIZEALL);
1668         SetCursor(hCursor);
1669         GlobalUnlock(hGlobal);
1670         return TRUE;
1671     }
1672 
1673     GetCursorPos(&pt1);
1674     pt2 = pt1;
1675     ScreenToClient(hWnd, &pt2);
1676 
1677     iKey = C1_HitTest(&pt2);
1678     if (iKey == -1)
1679         hCursor = LoadCursorW(0, (LPCWSTR)IDC_SIZEALL);
1680     else
1681         hCursor = LoadCursorW(0, (LPCWSTR)IDC_HAND);
1682     SetCursor(hCursor);
1683 
1684     if (HIWORD(lParam) == WM_LBUTTONDOWN)
1685     {
1686         pC1->pt1 = pt1;
1687         pC1->pt2 = pt2;
1688         pC1->iPressedKey = iKey;
1689         C1_OnButtonDown(hWnd, pC1);
1690     }
1691 
1692     GlobalUnlock(hGlobal);
1693     return TRUE;
1694 }
1695 
1696 static BOOL
C1_OnMouseMove(_In_ HWND hWnd,_In_ WPARAM wParam,_In_ LPARAM lParam)1697 C1_OnMouseMove(
1698     _In_ HWND hWnd,
1699     _In_ WPARAM wParam,
1700     _In_ LPARAM lParam)
1701 {
1702     HGLOBAL hGlobal;
1703     PC1WINDOW pC1;
1704     HDC hMemDC;
1705     DWORD dwFlags;
1706     INT iPressedKey;
1707     POINT pt;
1708     HGDIOBJ hbmOld;
1709     HDC hDC;
1710     INT iKey;
1711 
1712     hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
1713     pC1 = (PC1WINDOW)GlobalLock(hGlobal);
1714     if (!hGlobal || !pC1)
1715         return FALSE;
1716 
1717     if (pC1->dwFlags & FLAG_DRAGGING)
1718     {
1719         C1_DrawDragBorder(hWnd, &pC1->pt1, &pC1->pt2);
1720         GetCursorPos(&pC1->pt1);
1721         C1_DrawDragBorder(hWnd, &pC1->pt1, &pC1->pt2);
1722         GlobalUnlock(hGlobal);
1723         return TRUE;
1724     }
1725 
1726     if (pC1->iPressedKey != -1)
1727     {
1728         GetCursorPos(&pt);
1729         ScreenToClient(hWnd, &pt);
1730         iKey = C1_HitTest(&pt);
1731 
1732         hDC = GetDC(hWnd);
1733         hMemDC = CreateCompatibleDC(hDC);
1734         hbmOld = SelectObject(hMemDC, pC1->hbmKeyboard);
1735         dwFlags = pC1->dwFlags;
1736 
1737         iPressedKey = pC1->iPressedKey;
1738         if (!!(dwFlags & FLAG_PRESSED) == (iKey != iPressedKey))
1739         {
1740             if (iPressedKey != C1K_SHIFT || !(dwFlags & FLAG_SHIFT_PRESSED))
1741             {
1742                 C1_InvertButton(hDC, iPressedKey);
1743                 C1_InvertButton(hMemDC, pC1->iPressedKey);
1744             }
1745 
1746             pC1->dwFlags ^= FLAG_PRESSED;
1747         }
1748 
1749         SelectObject(hMemDC, hbmOld);
1750         DeleteDC(hMemDC);
1751         ReleaseDC(hWnd, hDC);
1752     }
1753 
1754     GlobalUnlock(hGlobal);
1755     return TRUE;
1756 }
1757 
1758 static BOOL
C1_OnButtonUp(_In_ HWND hWnd,_In_ WPARAM wParam,_In_ LPARAM lParam)1759 C1_OnButtonUp(
1760     _In_ HWND hWnd,
1761     _In_ WPARAM wParam,
1762     _In_ LPARAM lParam)
1763 {
1764     HGLOBAL hGlobal;
1765     PC1WINDOW pC1;
1766     BOOL ret = FALSE;
1767     INT x, y, iKey;
1768     HDC hDC, hMemDC;
1769     HGDIOBJ hbmOld;
1770     HIMC hIMC;
1771     HWND hwndOwner;
1772     LPINPUTCONTEXT pIC;
1773 
1774     hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
1775     pC1 = (PC1WINDOW)GlobalLock(hGlobal);
1776     if (!hGlobal || !pC1)
1777         return FALSE;
1778 
1779     ReleaseCapture();
1780 
1781     if (pC1->dwFlags & FLAG_DRAGGING)
1782     {
1783         pC1->dwFlags &= ~FLAG_DRAGGING;
1784         C1_DrawDragBorder(hWnd, &pC1->pt1, &pC1->pt2);
1785         x = pC1->pt1.x - pC1->pt2.x;
1786         y = pC1->pt1.y - pC1->pt2.y;
1787         SetWindowPos(hWnd, 0, x, y, 0, 0, 0x15u);
1788         ret = TRUE;
1789 
1790         hwndOwner = GetWindow(hWnd, GW_OWNER);
1791         hIMC = (HIMC)GetWindowLongPtrW(hwndOwner, 0);
1792         if (hIMC)
1793         {
1794             pIC = ImmLockIMC(hIMC);
1795             if (pIC)
1796             {
1797                 pIC->fdwInit |= INIT_SOFTKBDPOS;
1798                 pIC->ptSoftKbdPos.x = x;
1799                 pIC->ptSoftKbdPos.y = y;
1800                 ImmUnlockIMC(hIMC);
1801             }
1802         }
1803 
1804         GlobalUnlock(hGlobal);
1805         return ret;
1806     }
1807 
1808     iKey = pC1->iPressedKey;
1809     if (iKey == -1)
1810         return FALSE;
1811 
1812     if (!(pC1->dwFlags & FLAG_PRESSED))
1813     {
1814         pC1->iPressedKey = -1;
1815         GlobalUnlock(hGlobal);
1816         return ret;
1817     }
1818 
1819     if (iKey == C1K_SHIFT)
1820     {
1821         if (!(pC1->dwFlags & FLAG_SHIFT_PRESSED))
1822         {
1823             pC1->dwFlags |= FLAG_SHIFT_PRESSED;
1824             pC1->dwFlags &= ~FLAG_PRESSED;
1825             pC1->iPressedKey = -1;
1826             GlobalUnlock(hGlobal);
1827             return ret;
1828         }
1829     }
1830     else if (iKey < C1K_BACKSPACE && (pC1->dwFlags & FLAG_SHIFT_PRESSED))
1831     {
1832         INT iVK = gC1K2VK[pC1->iPressedKey];
1833         keybd_event(VK_SHIFT, guScanCode[C1K_SHIFT], 0, 0);
1834         keybd_event(iVK, guScanCode[(BYTE)iVK], 0, 0);
1835         keybd_event(iVK, guScanCode[(BYTE)iVK], KEYEVENTF_KEYUP, 0);
1836         keybd_event(VK_SHIFT, guScanCode[C1K_SHIFT], KEYEVENTF_KEYUP, 0);
1837     }
1838     else
1839     {
1840         INT iVK = gC1K2VK[iKey];
1841         keybd_event(iVK, guScanCode[iVK], 0, 0);
1842         keybd_event(iVK, guScanCode[iVK], KEYEVENTF_KEYUP, 0);
1843     }
1844 
1845     ret = TRUE;
1846 
1847     hDC = GetDC(hWnd);
1848     hMemDC = CreateCompatibleDC(hDC);
1849     hbmOld = SelectObject(hMemDC, pC1->hbmKeyboard);
1850 
1851     C1_InvertButton(hDC, pC1->iPressedKey);
1852     C1_InvertButton(hMemDC, pC1->iPressedKey);
1853 
1854     if (pC1->iPressedKey < C1K_BACKSPACE && (pC1->dwFlags & FLAG_SHIFT_PRESSED))
1855     {
1856         C1_InvertButton(hDC, C1K_SHIFT);
1857         C1_InvertButton(hMemDC, C1K_SHIFT);
1858     }
1859 
1860     if (pC1->iPressedKey < C1K_BACKSPACE || pC1->iPressedKey == C1K_SHIFT)
1861         pC1->dwFlags &= ~FLAG_SHIFT_PRESSED;
1862 
1863     SelectObject(hMemDC, hbmOld);
1864     DeleteDC(hMemDC);
1865     ReleaseDC(hWnd, hDC);
1866 
1867     pC1->dwFlags &= ~FLAG_PRESSED;
1868     pC1->iPressedKey = -1;
1869     GlobalUnlock(hGlobal);
1870     return ret;
1871 }
1872 
1873 static void
C1_OnDestroy(_In_ HWND hWnd)1874 C1_OnDestroy(
1875     _In_ HWND hWnd)
1876 {
1877     HGLOBAL hGlobal;
1878     PC1WINDOW pC1;
1879     HWND hwndOwner;
1880 
1881     hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
1882     pC1 = (PC1WINDOW)GlobalLock(hGlobal);
1883     if (!hGlobal || !pC1)
1884         return;
1885 
1886     if (pC1->dwFlags & FLAG_DRAGGING)
1887         C1_DrawDragBorder(hWnd, &pC1->pt1, &pC1->pt2);
1888 
1889     DeleteObject(pC1->hbmKeyboard);
1890     GlobalUnlock(hGlobal);
1891     GlobalFree(hGlobal);
1892 
1893     hwndOwner = GetWindow(hWnd, GW_OWNER);
1894     if (hwndOwner)
1895         SendMessageW(hwndOwner, WM_IME_NOTIFY, IMN_SOFTKBDDESTROYED, 0);
1896 }
1897 
1898 static LRESULT
C1_OnImeControl(_In_ HWND hWnd,_In_ WPARAM wParam,_In_ LPARAM lParam)1899 C1_OnImeControl(
1900     _In_ HWND hWnd,
1901     _In_ WPARAM wParam,
1902     _In_ LPARAM lParam)
1903 {
1904     HGLOBAL hGlobal;
1905     PC1WINDOW pC1;
1906     LOGFONTW lf;
1907     RECT rc;
1908     LRESULT ret = 0;
1909     HDC hDC;
1910 
1911     switch (wParam)
1912     {
1913         case IMC_GETSOFTKBDFONT:
1914         {
1915             TRACE("IMC_GETSOFTKBDFONT: %p\n", lParam);
1916             hDC = GetDC(hWnd);
1917             GetObjectW(GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONTW), &lf);
1918             ReleaseDC(hWnd, hDC);
1919             *(LPLOGFONTW)lParam = lf;
1920             break;
1921         }
1922         case IMC_SETSOFTKBDFONT:
1923         {
1924             LPLOGFONTW plf = (LPLOGFONTW)lParam;
1925             LOGFONTW lf;
1926             TRACE("IMC_SETSOFTKBDFONT: %p\n", lParam);
1927             GetObjectW(GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONTW), &lf);
1928             if (lf.lfCharSet == plf->lfCharSet)
1929                 return 0;
1930 
1931             hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
1932             pC1 = (PC1WINDOW)GlobalLock(hGlobal);
1933             if (!hGlobal || !pC1)
1934                 return 1;
1935 
1936             pC1->CharSet = plf->lfCharSet;
1937             GlobalUnlock(hGlobal);
1938             break;
1939         }
1940         case IMC_GETSOFTKBDPOS:
1941         {
1942             TRACE("IMC_GETSOFTKBDPOS\n");
1943             GetWindowRect(hWnd, &rc);
1944             return MAKELRESULT(rc.left, rc.top);
1945         }
1946         case IMC_SETSOFTKBDPOS:
1947         {
1948             POINT pt;
1949             POINTSTOPOINT(pt, lParam);
1950             TRACE("IMC_SETSOFTKBDPOS: %d, %d\n", pt.x, pt.y);
1951             SetWindowPos(hWnd, NULL, pt.x, pt.y, 0, 0,
1952                          (SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE));
1953             break;
1954         }
1955         case IMC_GETSOFTKBDSUBTYPE:
1956         case IMC_SETSOFTKBDSUBTYPE:
1957         {
1958             TRACE("IMC_GETSOFTKBDSUBTYPE/IMC_SETSOFTKBDSUBTYPE: %p, %p\n", wParam, lParam);
1959             hGlobal = (HGLOBAL)GetWindowLongPtrW(hWnd, 0);
1960             pC1 = (PC1WINDOW)GlobalLock(hGlobal);
1961             if (!hGlobal || !pC1)
1962                 return -1;
1963             ret = pC1->SubType;
1964             if (wParam == IMC_SETSOFTKBDSUBTYPE)
1965                 pC1->SubType = lParam;
1966             GlobalUnlock(hGlobal);
1967             break;
1968         }
1969         case IMC_SETSOFTKBDDATA:
1970         {
1971             TRACE("IMC_SETSOFTKBDDATA: %p\n", lParam);
1972             if (C1_SetData(hWnd, (SOFTKBDDATA*)lParam))
1973                 return -1;
1974 
1975             InvalidateRect(hWnd, 0, 0);
1976         }
1977         default:
1978             break;
1979     }
1980 
1981     return ret;
1982 }
1983 
1984 static LRESULT CALLBACK
C1_WindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)1985 C1_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1986 {
1987     switch (uMsg)
1988     {
1989         case WM_CREATE:
1990         {
1991             return C1_OnCreate(hWnd);
1992         }
1993         case WM_DESTROY:
1994         {
1995             C1_OnDestroy(hWnd);
1996             break;
1997         }
1998         case WM_SETCURSOR:
1999         {
2000             if (C1_OnSetCursor(hWnd, lParam))
2001                 break;
2002             return DefWindowProcW(hWnd, uMsg, wParam, lParam);
2003         }
2004         case WM_MOUSEMOVE:
2005         {
2006             if (C1_OnMouseMove(hWnd, wParam, lParam))
2007                 break;
2008             return DefWindowProcW(hWnd, uMsg, wParam, lParam);
2009         }
2010         case WM_LBUTTONUP:
2011         {
2012             if (C1_OnButtonUp(hWnd, wParam, lParam))
2013                 break;
2014             return DefWindowProcW(hWnd, uMsg, wParam, lParam);
2015         }
2016         case WM_PAINT:
2017         {
2018             PAINTSTRUCT ps;
2019             HDC hDC = BeginPaint(hWnd, &ps);
2020             C1_OnDraw(hDC, hWnd);
2021             EndPaint(hWnd, &ps);
2022             break;
2023         }
2024         case WM_IME_CONTROL:
2025         {
2026             return C1_OnImeControl(hWnd, wParam, lParam);
2027         }
2028         case WM_MOUSEACTIVATE:
2029         {
2030             return MA_NOACTIVATE;
2031         }
2032         default:
2033         {
2034             return DefWindowProcW(hWnd, uMsg, wParam, lParam);
2035         }
2036     }
2037     return 0;
2038 }
2039 
2040 /*****************************************************************************/
2041 
2042 static BOOL
Imm32RegisterSoftKeyboard(_In_ UINT uType)2043 Imm32RegisterSoftKeyboard(
2044     _In_ UINT uType)
2045 {
2046     WNDCLASSEXW wcx;
2047     LPCWSTR pszClass = ((uType == SOFTKEYBOARD_TYPE_T1) ? T1_CLASSNAMEW : C1_CLASSNAMEW);
2048     if (GetClassInfoExW(ghImm32Inst, pszClass, &wcx))
2049         return TRUE;
2050 
2051     ZeroMemory(&wcx, sizeof(wcx));
2052     wcx.cbSize        = sizeof(wcx);
2053     wcx.style         = CS_IME;
2054     wcx.cbWndExtra    = sizeof(PT1WINDOW);
2055     wcx.hIcon         = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
2056     wcx.hInstance     = ghImm32Inst;
2057     wcx.hCursor       = LoadCursorW(NULL, (LPCWSTR)IDC_SIZEALL);
2058     wcx.lpszClassName = pszClass;
2059 
2060     if (uType == SOFTKEYBOARD_TYPE_T1)
2061     {
2062         wcx.lpfnWndProc = T1_WindowProc;
2063         wcx.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
2064     }
2065     else
2066     {
2067         wcx.lpfnWndProc = C1_WindowProc;
2068         wcx.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH);
2069     }
2070 
2071     return !!RegisterClassExW(&wcx);
2072 }
2073 
2074 static void
Imm32GetSoftKeyboardDimension(_In_ UINT uType,_Out_ LPINT pcx,_Out_ LPINT pcy)2075 Imm32GetSoftKeyboardDimension(
2076     _In_ UINT uType,
2077     _Out_ LPINT pcx,
2078     _Out_ LPINT pcy)
2079 {
2080     if (uType == SOFTKEYBOARD_TYPE_T1)
2081     {
2082         TEXTMETRICW tm;
2083         T1_GetTextMetric(&tm);
2084         *pcx = 15 * tm.tmMaxCharWidth + 2 * gptRaiseEdge.x + 139;
2085         *pcy = 5 * tm.tmHeight + 2 * gptRaiseEdge.y + 58;
2086     }
2087     else
2088     {
2089         INT cxEdge = GetSystemMetrics(SM_CXEDGE), cyEdge = GetSystemMetrics(SM_CXEDGE);
2090         *pcx = 2 * (GetSystemMetrics(SM_CXBORDER) + cxEdge) + 348;
2091         *pcy = 2 * (GetSystemMetrics(SM_CYBORDER) + cyEdge) + 136;
2092     }
2093 }
2094 
2095 /***********************************************************************
2096  *		ImmCreateSoftKeyboard (IMM32.@)
2097  *
2098  * @see https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/ImmCreateSoftKeyboard.html
2099  */
2100 HWND WINAPI
ImmCreateSoftKeyboard(_In_ UINT uType,_In_ HWND hwndParent,_In_ INT x,_In_ INT y)2101 ImmCreateSoftKeyboard(
2102     _In_ UINT uType,
2103     _In_ HWND hwndParent,
2104     _In_ INT x,
2105     _In_ INT y)
2106 {
2107     HKL hKL;
2108     PIMEDPI pImeDpi;
2109     UINT iVK;
2110     INT xSoftKBD, ySoftKBD, cxSoftKBD, cySoftKBD, cxEdge, cyEdge;
2111     HWND hwndSoftKBD;
2112     DWORD Style, ExStyle, UICaps;
2113     LPCWSTR pszClass;
2114     RECT rcWorkArea;
2115 
2116     TRACE("(%u, %p, %d, %d)\n", uType, hwndParent, x, y);
2117 
2118     if ((uType != SOFTKEYBOARD_TYPE_T1) && (uType != SOFTKEYBOARD_TYPE_C1))
2119     {
2120         ERR("uType: %u\n", uType);
2121         return NULL; /* Invalid keyboard type */
2122     }
2123 
2124     /* Check IME */
2125     hKL = GetKeyboardLayout(0);
2126     pImeDpi = ImmLockImeDpi(hKL);
2127     if (IS_NULL_UNEXPECTEDLY(pImeDpi))
2128         return NULL; /* No IME */
2129 
2130     UICaps = pImeDpi->ImeInfo.fdwUICaps;
2131     ImmUnlockImeDpi(pImeDpi);
2132 
2133     /* Check IME capability */
2134     if (!(UICaps & UI_CAP_SOFTKBD))
2135     {
2136         ERR("UICaps: 0x%X\n", UICaps);
2137         return NULL; /* No capability for soft keyboard */
2138     }
2139 
2140     /* Want metrics? */
2141     if (g_bWantSoftKBDMetrics)
2142     {
2143         for (iVK = 0; iVK < 0xFF; ++iVK)
2144         {
2145             guScanCode[iVK] = MapVirtualKeyW(iVK, 0);
2146         }
2147 
2148         cxEdge = GetSystemMetrics(SM_CXEDGE);
2149         cyEdge = GetSystemMetrics(SM_CYEDGE);
2150         gptRaiseEdge.x = GetSystemMetrics(SM_CXBORDER) + cxEdge;
2151         gptRaiseEdge.y = GetSystemMetrics(SM_CYBORDER) + cyEdge;
2152 
2153         g_bWantSoftKBDMetrics = FALSE;
2154     }
2155 
2156     if (!Imm32GetNearestWorkArea(hwndParent, &rcWorkArea))
2157         return NULL;
2158 
2159     /* Register the window class */
2160     if (!Imm32RegisterSoftKeyboard(uType))
2161     {
2162         ERR("\n");
2163         return NULL;
2164     }
2165 
2166     /* Calculate keyboard size */
2167     Imm32GetSoftKeyboardDimension(uType, &cxSoftKBD, &cySoftKBD);
2168 
2169     /* Adjust keyboard position */
2170     xSoftKBD = Imm32Clamp(x, rcWorkArea.left, rcWorkArea.right  - cxSoftKBD);
2171     ySoftKBD = Imm32Clamp(y, rcWorkArea.top , rcWorkArea.bottom - cySoftKBD);
2172 
2173     /* Create soft keyboard window */
2174     if (uType == SOFTKEYBOARD_TYPE_T1)
2175     {
2176         Style = (WS_POPUP | WS_DISABLED);
2177         ExStyle = 0;
2178         pszClass = T1_CLASSNAMEW;
2179     }
2180     else
2181     {
2182         Style = (WS_POPUP | WS_DISABLED | WS_BORDER);
2183         ExStyle = (WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME);
2184         pszClass = C1_CLASSNAMEW;
2185     }
2186     hwndSoftKBD = CreateWindowExW(ExStyle, pszClass, NULL, Style,
2187                                   xSoftKBD, ySoftKBD, cxSoftKBD, cySoftKBD,
2188                                   hwndParent, NULL, ghImm32Inst, NULL);
2189     /* Initial is hidden */
2190     ShowWindow(hwndSoftKBD, SW_HIDE);
2191     UpdateWindow(hwndSoftKBD);
2192 
2193     return hwndSoftKBD;
2194 }
2195 
2196 /***********************************************************************
2197  *		ImmShowSoftKeyboard (IMM32.@)
2198  *
2199  * @see https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/ImmShowSoftKeyboard.html
2200  */
2201 BOOL WINAPI
ImmShowSoftKeyboard(_In_ HWND hwndSoftKBD,_In_ INT nCmdShow)2202 ImmShowSoftKeyboard(
2203     _In_ HWND hwndSoftKBD,
2204     _In_ INT nCmdShow)
2205 {
2206     TRACE("(%p, %d)\n", hwndSoftKBD, nCmdShow);
2207 
2208     if (nCmdShow != SW_HIDE && nCmdShow != SW_SHOWNOACTIVATE)
2209         WARN("nCmdShow %d is unexpected\n", nCmdShow);
2210 
2211     return hwndSoftKBD && ShowWindow(hwndSoftKBD, nCmdShow);
2212 }
2213 
2214 /***********************************************************************
2215  *		ImmDestroySoftKeyboard (IMM32.@)
2216  *
2217  * @see https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/ImmDestroySoftKeyboard.html
2218  */
2219 BOOL WINAPI
ImmDestroySoftKeyboard(_In_ HWND hwndSoftKBD)2220 ImmDestroySoftKeyboard(
2221     _In_ HWND hwndSoftKBD)
2222 {
2223     TRACE("(%p)\n", hwndSoftKBD);
2224     return DestroyWindow(hwndSoftKBD);
2225 }
2226