1 /* 2 * PROJECT: ReactOS On-Screen Keyboard 3 * LICENSE: GPL - See COPYING in the top level directory 4 * PURPOSE: On-screen keyboard. 5 * COPYRIGHT: Denis ROBERT 6 * Copyright 2019-2020 George Bișoc (george.bisoc@reactos.org) 7 * Baruch Rutman (peterooch at gmail dot com) 8 */ 9 10 /* INCLUDES *******************************************************************/ 11 12 #include "precomp.h" 13 14 /* GLOBALS ********************************************************************/ 15 16 OSK_GLOBALS Globals; 17 18 OSK_KEYLEDINDICATOR LedKey[] = 19 { 20 {VK_NUMLOCK, IDC_LED_NUM, 0x0145, FALSE}, 21 {VK_CAPITAL, IDC_LED_CAPS, 0x013A, FALSE}, 22 {VK_SCROLL, IDC_LED_SCROLL, 0x0146, FALSE} 23 }; 24 25 /* FUNCTIONS ******************************************************************/ 26 27 /*********************************************************************** 28 * 29 * OSK_SetImage 30 * 31 * Set an image on a button 32 */ 33 int OSK_SetImage(int IdDlgItem, int IdResource) 34 { 35 HICON hIcon; 36 HWND hWndItem; 37 38 hIcon = (HICON)LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IdResource), 39 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR); 40 if (hIcon == NULL) 41 return FALSE; 42 43 hWndItem = GetDlgItem(Globals.hMainWnd, IdDlgItem); 44 if (hWndItem == NULL) 45 { 46 DestroyIcon(hIcon); 47 return FALSE; 48 } 49 50 SendMessageW(hWndItem, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hIcon); 51 52 /* The system automatically deletes these resources when the process that created them terminates (MSDN) */ 53 54 return TRUE; 55 } 56 57 /*********************************************************************** 58 * 59 * OSK_SetText 60 * 61 * Update the text of a button according to the relevant language resource 62 */ 63 void OSK_SetText(int IdDlgItem, int IdResource) 64 { 65 WCHAR szText[MAX_PATH]; 66 HWND hWndItem; 67 68 hWndItem = GetDlgItem(Globals.hMainWnd, IdDlgItem); 69 70 if (hWndItem == NULL) 71 return; 72 73 LoadStringW(Globals.hInstance, IdResource, szText, _countof(szText)); 74 75 SetWindowTextW(hWndItem, szText); 76 } 77 /*********************************************************************** 78 * 79 * OSK_WarningProc 80 * 81 * Function handler for the warning dialog box on startup 82 */ 83 INT_PTR CALLBACK OSK_WarningProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam) 84 { 85 UNREFERENCED_PARAMETER(lParam); 86 87 switch (Msg) 88 { 89 case WM_INITDIALOG: 90 { 91 return TRUE; 92 } 93 94 case WM_COMMAND: 95 { 96 switch (LOWORD(wParam)) 97 { 98 case IDC_SHOWWARNINGCHECK: 99 { 100 Globals.bShowWarning = !IsDlgButtonChecked(hDlg, IDC_SHOWWARNINGCHECK); 101 return TRUE; 102 } 103 104 case IDOK: 105 case IDCANCEL: 106 { 107 EndDialog(hDlg, LOWORD(wParam)); 108 return TRUE; 109 } 110 } 111 break; 112 } 113 } 114 115 return FALSE; 116 } 117 118 /*********************************************************************** 119 * 120 * OSK_WarningDlgThread 121 * 122 * Thread procedure routine for the warning dialog box 123 */ 124 DWORD WINAPI OSK_WarningDlgThread(LPVOID lpParameter) 125 { 126 HINSTANCE hInstance = (HINSTANCE)lpParameter; 127 128 DialogBoxW(hInstance, MAKEINTRESOURCEW(IDD_WARNINGDIALOG_OSK), Globals.hMainWnd, OSK_WarningProc); 129 return 0; 130 } 131 132 /*********************************************************************** 133 * 134 * OSK_About 135 * 136 * Initializes the "About" dialog box 137 */ 138 VOID OSK_About(VOID) 139 { 140 WCHAR szAuthors[MAX_PATH]; 141 142 /* Load the strings into the "About" dialog */ 143 LoadStringW(Globals.hInstance, IDS_AUTHORS, szAuthors, _countof(szAuthors)); 144 145 /* Load the icon */ 146 /* Finally, execute the "About" dialog by using the Shell routine */ 147 ShellAboutW(Globals.hMainWnd, Globals.szTitle, szAuthors, 148 LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IDI_OSK), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED)); 149 } 150 151 /*********************************************************************** 152 * 153 * OSK_DestroyKeys 154 * 155 * Used in layout change or in shutdown 156 */ 157 VOID OSK_DestroyKeys(VOID) 158 { 159 int i; 160 /* Hide before destroying child controls */ 161 ShowWindow(Globals.hMainWnd, SW_HIDE); 162 163 for (i = 0; i < Globals.Keyboard->KeyCount; i++) 164 { 165 DestroyWindow(Globals.hKeys[i]); 166 } 167 for (i = 0; i < _countof(LedKey); i++) 168 { 169 DestroyWindow(GetDlgItem(Globals.hMainWnd, LedKey[i].DlgResource)); 170 } 171 172 HeapFree(GetProcessHeap(), 0, Globals.hKeys); 173 Globals.hKeys = NULL; 174 Globals.Keyboard = NULL; 175 } 176 177 /*********************************************************************** 178 * 179 * OSK_SetKeys 180 * 181 * Create/Update button controls with the relevant keyboard values 182 */ 183 LRESULT OSK_SetKeys(int reason) 184 { 185 WCHAR wKey[2]; 186 BYTE bKeyStates[256]; 187 LPCWSTR szKey; 188 PKEY Keys; 189 UINT uVirtKey; 190 POINT LedPos; 191 SIZE LedSize; 192 int i, yPad; 193 194 /* Get key states before doing anything */ 195 if (!GetKeyboardState(bKeyStates)) 196 { 197 DPRINT("OSK_SetKeys(): GetKeyboardState() call failed.\n"); 198 return -1; 199 } 200 201 switch (reason) 202 { 203 case SETKEYS_LANG: 204 { 205 /* Keyboard language/caps change, just update the button texts */ 206 Keys = Globals.Keyboard->Keys; 207 for (i = 0; i < Globals.Keyboard->KeyCount; i++) 208 { 209 if (!Keys[i].translate) 210 continue; 211 212 uVirtKey = MapVirtualKeyW(Keys[i].scancode & SCANCODE_MASK, MAPVK_VSC_TO_VK); 213 214 if (ToUnicode(uVirtKey, Keys[i].scancode & SCANCODE_MASK, bKeyStates, wKey, _countof(wKey), 0) >= 1) 215 { 216 szKey = wKey; 217 } 218 else 219 { 220 szKey = Keys[i].name; 221 } 222 223 /* Only one & the button will try to underline the next character... */ 224 if (wcsncmp(szKey, L"&", 1) == 0) 225 szKey = L"&&"; 226 227 SetWindowTextW(Globals.hKeys[i], szKey); 228 } 229 return 0; 230 } 231 case SETKEYS_LAYOUT: 232 { 233 /* Clear up current layout before applying a different one */ 234 OSK_DestroyKeys(); 235 } 236 /* Fallthrough */ 237 case SETKEYS_INIT: 238 { 239 if (Globals.bIsEnhancedKeyboard) 240 { 241 Globals.Keyboard = &EnhancedKeyboard; 242 } 243 else 244 { 245 Globals.Keyboard = &StandardKeyboard; 246 } 247 248 Globals.hKeys = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HWND) * Globals.Keyboard->KeyCount); 249 250 if (!Globals.hKeys) 251 { 252 DPRINT("OSK_SetKeys(): Failed to allocate memory for button handles.\n"); 253 return -1; 254 } 255 256 Keys = Globals.Keyboard->Keys; 257 258 /* Create key buttons */ 259 for (i = 0; i < Globals.Keyboard->KeyCount; i++) 260 { 261 uVirtKey = MapVirtualKeyW(Keys[i].scancode & SCANCODE_MASK, MAPVK_VSC_TO_VK); 262 263 if (Keys[i].translate && ToUnicode(uVirtKey, Keys[i].scancode & SCANCODE_MASK, bKeyStates, wKey, _countof(wKey), 0) >= 1) 264 { 265 szKey = wKey; 266 } 267 else 268 { 269 szKey = Keys[i].name; 270 } 271 272 Globals.hKeys[i] = CreateWindowW(WC_BUTTONW, 273 szKey, 274 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON | Keys[i].flags, 275 Keys[i].x, 276 Keys[i].y, 277 Keys[i].cx, 278 Keys[i].cy, 279 Globals.hMainWnd, 280 (HMENU)Keys[i].scancode, 281 Globals.hInstance, 282 NULL); 283 if (Globals.hFont) 284 SendMessageW(Globals.hKeys[i], WM_SETFONT, (WPARAM)Globals.hFont, 0); 285 } 286 287 /* Add additional padding for caption and menu */ 288 yPad = GetSystemMetrics(SM_CYSIZE) + GetSystemMetrics(SM_CYMENU); 289 /* Size window according to layout */ 290 SetWindowPos(Globals.hMainWnd, 291 (Globals.bAlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST), 292 0, 293 0, 294 Globals.Keyboard->Size.cx, 295 Globals.Keyboard->Size.cy + yPad, 296 SWP_NOMOVE); 297 298 /* Create LEDs */ 299 LedPos = Globals.Keyboard->LedStart; 300 LedSize = Globals.Keyboard->LedSize; 301 302 CreateWindowW(WC_STATICW, L"", WS_VISIBLE | WS_CHILD | SS_CENTER | SS_NOTIFY, 303 LedPos.x, LedPos.y, LedSize.cx, LedSize.cy, Globals.hMainWnd, 304 (HMENU)IDC_LED_NUM, Globals.hInstance, NULL); 305 306 LedPos.x += Globals.Keyboard->LedGap; 307 308 CreateWindowW(WC_STATICW, L"", WS_VISIBLE | WS_CHILD | SS_CENTER | SS_NOTIFY, 309 LedPos.x, LedPos.y, LedSize.cx, LedSize.cy, Globals.hMainWnd, 310 (HMENU)IDC_LED_CAPS, Globals.hInstance, NULL); 311 312 LedPos.x += Globals.Keyboard->LedGap; 313 314 CreateWindowW(WC_STATICW, L"", WS_VISIBLE | WS_CHILD | SS_CENTER | SS_NOTIFY, 315 LedPos.x, LedPos.y, LedSize.cx, LedSize.cy, Globals.hMainWnd, 316 (HMENU)IDC_LED_SCROLL, Globals.hInstance, NULL); 317 318 /* Set system keys text */ 319 OSK_SetText(SCAN_CODE_110, IDS_ESCAPE); 320 OSK_SetText(SCAN_CODE_124, IDS_PRN); 321 OSK_SetText(SCAN_CODE_125, IDS_STOP); 322 OSK_SetText(SCAN_CODE_126, IDS_ATTN); 323 OSK_SetText(SCAN_CODE_90, IDS_NUMLOCKKEY); 324 OSK_SetText(SCAN_CODE_75, IDS_INSERT); 325 OSK_SetText(SCAN_CODE_76, IDS_DELETE); 326 OSK_SetText(SCAN_CODE_81, IDS_END); 327 OSK_SetText(SCAN_CODE_58, IDS_CTRL); /* Left ctrl */ 328 OSK_SetText(SCAN_CODE_64, IDS_CTRL); /* Right ctrl */ 329 OSK_SetText(SCAN_CODE_60, IDS_LEFTALT); 330 OSK_SetText(SCAN_CODE_62, IDS_RIGHTALT); 331 332 /* Set icon on visual buttons */ 333 OSK_SetImage(SCAN_CODE_15, IDI_BACK); 334 OSK_SetImage(SCAN_CODE_16, IDI_TAB); 335 OSK_SetImage(SCAN_CODE_30, IDI_CAPS_LOCK); 336 OSK_SetImage(SCAN_CODE_43, IDI_RETURN); 337 OSK_SetImage(SCAN_CODE_44, IDI_SHIFT); 338 OSK_SetImage(SCAN_CODE_57, IDI_SHIFT); 339 OSK_SetImage(SCAN_CODE_127, IDI_REACTOS); 340 OSK_SetImage(SCAN_CODE_128, IDI_REACTOS); 341 OSK_SetImage(SCAN_CODE_129, IDI_MENU); 342 OSK_SetImage(SCAN_CODE_80, IDI_HOME); 343 OSK_SetImage(SCAN_CODE_85, IDI_PG_UP); 344 OSK_SetImage(SCAN_CODE_86, IDI_PG_DOWN); 345 OSK_SetImage(SCAN_CODE_79, IDI_LEFT); 346 OSK_SetImage(SCAN_CODE_83, IDI_TOP); 347 OSK_SetImage(SCAN_CODE_84, IDI_BOTTOM); 348 OSK_SetImage(SCAN_CODE_89, IDI_RIGHT); 349 } 350 } 351 352 if (reason != SETKEYS_INIT) 353 { 354 ShowWindow(Globals.hMainWnd, SW_SHOW); 355 UpdateWindow(Globals.hMainWnd); 356 } 357 358 return 0; 359 } 360 361 /*********************************************************************** 362 * 363 * OSK_Create 364 * 365 * Handling of WM_CREATE 366 */ 367 LRESULT OSK_Create(HWND hwnd) 368 { 369 HMONITOR monitor; 370 MONITORINFO info; 371 POINT Pt; 372 RECT rcWindow, rcDlgIntersect; 373 LOGFONTW lf = {0}; 374 375 /* Save handle */ 376 Globals.hMainWnd = hwnd; 377 378 /* Init Font */ 379 lf.lfHeight = Globals.FontHeight; 380 StringCchCopyW(lf.lfFaceName, _countof(Globals.FontFaceName), Globals.FontFaceName); 381 Globals.hFont = CreateFontIndirectW(&lf); 382 383 if (OSK_SetKeys(SETKEYS_INIT) == -1) 384 return -1; 385 386 /* Check the checked menu item before displaying the window */ 387 if (Globals.bIsEnhancedKeyboard) 388 { 389 /* Enhanced keyboard dialog chosen, set the respective menu item as checked */ 390 CheckMenuItem(GetMenu(hwnd), IDM_ENHANCED_KB, MF_BYCOMMAND | MF_CHECKED); 391 CheckMenuItem(GetMenu(hwnd), IDM_STANDARD_KB, MF_BYCOMMAND | MF_UNCHECKED); 392 } 393 else 394 { 395 /* Standard keyboard dialog chosen, set the respective menu item as checked */ 396 CheckMenuItem(GetMenu(hwnd), IDM_STANDARD_KB, MF_BYCOMMAND | MF_CHECKED); 397 CheckMenuItem(GetMenu(hwnd), IDM_ENHANCED_KB, MF_BYCOMMAND | MF_UNCHECKED); 398 } 399 400 /* Check if the "Click Sound" option was chosen before (and if so, then tick the menu item) */ 401 if (Globals.bSoundClick) 402 { 403 CheckMenuItem(GetMenu(hwnd), IDM_CLICK_SOUND, MF_BYCOMMAND | MF_CHECKED); 404 } 405 406 /* Get screen info */ 407 memset(&Pt, 0, sizeof(Pt)); 408 monitor = MonitorFromPoint(Pt, MONITOR_DEFAULTTOPRIMARY); 409 info.cbSize = sizeof(info); 410 GetMonitorInfoW(monitor, &info); 411 GetWindowRect(hwnd, &rcWindow); 412 413 /* 414 If the coordination values are default then re-initialize using the specific formulas 415 to move the dialog at the bottom of the screen. 416 */ 417 if (Globals.PosX == CW_USEDEFAULT && Globals.PosY == CW_USEDEFAULT) 418 { 419 Globals.PosX = (info.rcMonitor.left + info.rcMonitor.right - (rcWindow.right - rcWindow.left)) / 2; 420 Globals.PosY = info.rcMonitor.bottom - (rcWindow.bottom - rcWindow.top); 421 } 422 423 /* 424 Calculate the intersection of two rectangle sources (dialog and work desktop area). 425 If such sources do not intersect, then the dialog is deemed as "off screen". 426 */ 427 if (IntersectRect(&rcDlgIntersect, &rcWindow, &info.rcWork) == 0) 428 { 429 Globals.PosX = (info.rcMonitor.left + info.rcMonitor.right - (rcWindow.right - rcWindow.left)) / 2; 430 Globals.PosY = info.rcMonitor.bottom - (rcWindow.bottom - rcWindow.top); 431 } 432 else 433 { 434 /* 435 There's still some intersection but we're not for sure if it is sufficient (the dialog could also be partially hidden). 436 Therefore, check the remaining intersection if it's enough. 437 */ 438 if (rcWindow.top < info.rcWork.top || rcWindow.left < info.rcWork.left || rcWindow.right > info.rcWork.right || rcWindow.bottom > info.rcWork.bottom) 439 { 440 Globals.PosX = (info.rcMonitor.left + info.rcMonitor.right - (rcWindow.right - rcWindow.left)) / 2; 441 Globals.PosY = info.rcMonitor.bottom - (rcWindow.bottom - rcWindow.top); 442 } 443 } 444 445 /* 446 Place the window (with respective placement coordinates) as topmost, above 447 every window which are not on top or are at the bottom of the Z order. 448 */ 449 if (Globals.bAlwaysOnTop) 450 { 451 CheckMenuItem(GetMenu(hwnd), IDM_ON_TOP, MF_BYCOMMAND | MF_CHECKED); 452 SetWindowPos(hwnd, HWND_TOPMOST, Globals.PosX, Globals.PosY, 0, 0, SWP_NOSIZE); 453 } 454 else 455 { 456 CheckMenuItem(GetMenu(hwnd), IDM_ON_TOP, MF_BYCOMMAND | MF_UNCHECKED); 457 SetWindowPos(hwnd, HWND_NOTOPMOST, Globals.PosX, Globals.PosY, 0, 0, SWP_NOSIZE); 458 } 459 460 /* Create a green brush for leds */ 461 Globals.hBrushGreenLed = CreateSolidBrush(RGB(0, 255, 0)); 462 463 /* Set a timer for periodic tasks */ 464 Globals.iTimer = SetTimer(hwnd, 0, 100, NULL); 465 466 /* If the member of the struct (bShowWarning) is set then display the dialog box */ 467 if (Globals.bShowWarning) 468 { 469 /* If for whatever reason the thread fails to be created then handle the dialog box in main thread... */ 470 if (CreateThread(NULL, 0, OSK_WarningDlgThread, (PVOID)Globals.hInstance, 0, NULL) == NULL) 471 { 472 DialogBoxW(Globals.hInstance, MAKEINTRESOURCEW(IDD_WARNINGDIALOG_OSK), Globals.hMainWnd, OSK_WarningProc); 473 } 474 } 475 476 return 0; 477 } 478 479 /*********************************************************************** 480 * 481 * OSK_Close 482 * 483 * Handling of WM_CLOSE 484 */ 485 int OSK_Close(void) 486 { 487 KillTimer(Globals.hMainWnd, Globals.iTimer); 488 489 /* Release Ctrl, Shift, Alt keys */ 490 OSK_ReleaseKey(SCAN_CODE_44); // Left shift 491 OSK_ReleaseKey(SCAN_CODE_57); // Right shift 492 OSK_ReleaseKey(SCAN_CODE_58); // Left ctrl 493 OSK_ReleaseKey(SCAN_CODE_60); // Left alt 494 OSK_ReleaseKey(SCAN_CODE_62); // Right alt 495 OSK_ReleaseKey(SCAN_CODE_64); // Right ctrl 496 497 /* Destroy child controls */ 498 OSK_DestroyKeys(); 499 500 /* delete GDI objects */ 501 if (Globals.hBrushGreenLed) DeleteObject(Globals.hBrushGreenLed); 502 if (Globals.hFont) DeleteObject(Globals.hFont); 503 504 /* Save the application's settings on registry */ 505 SaveSettings(); 506 507 return TRUE; 508 } 509 510 /*********************************************************************** 511 * 512 * OSK_RefreshLEDKeys 513 * 514 * Updates (invalidates) the LED icon resources then the respective 515 * keys (Caps Lock, Scroll Lock or Num Lock) are being held down 516 */ 517 VOID OSK_RefreshLEDKeys(VOID) 518 { 519 INT i; 520 BOOL bKeyIsPressed; 521 522 for (i = 0; i < _countof(LedKey); i++) 523 { 524 bKeyIsPressed = (GetAsyncKeyState(LedKey[i].vKey) & 0x8000) != 0; 525 if (LedKey[i].bWasKeyPressed != bKeyIsPressed) 526 { 527 LedKey[i].bWasKeyPressed = bKeyIsPressed; 528 InvalidateRect(GetDlgItem(Globals.hMainWnd, LedKey[i].DlgResource), NULL, FALSE); 529 } 530 } 531 } 532 533 /*********************************************************************** 534 * 535 * OSK_Timer 536 * 537 * Handling of WM_TIMER 538 */ 539 int OSK_Timer(void) 540 { 541 HWND hWndActiveWindow; 542 DWORD dwThread; 543 HKL hKeyboardLayout; 544 545 hWndActiveWindow = GetForegroundWindow(); 546 if (hWndActiveWindow != NULL && hWndActiveWindow != Globals.hMainWnd) 547 { 548 /* Grab the current keyboard layout from the foreground window */ 549 dwThread = GetWindowThreadProcessId(hWndActiveWindow, NULL); 550 hKeyboardLayout = GetKeyboardLayout(dwThread); 551 /* Activate the layout */ 552 ActivateKeyboardLayout(hKeyboardLayout, 0); 553 } 554 555 /* 556 Update the LED key indicators accordingly to their state (if one 557 of the specific keys is held down). 558 */ 559 OSK_RefreshLEDKeys(); 560 /* Update the buttons */ 561 OSK_SetKeys(SETKEYS_LANG); 562 563 return TRUE; 564 } 565 566 /*********************************************************************** 567 * 568 * OSK_ChooseFont 569 * 570 * Change the font of which the keys are being displayed 571 */ 572 VOID OSK_ChooseFont(VOID) 573 { 574 LOGFONTW lf = {0}; 575 CHOOSEFONTW cf = {0}; 576 HFONT hFont, hOldFont; 577 int i; 578 579 StringCchCopyW(lf.lfFaceName, _countof(Globals.FontFaceName), Globals.FontFaceName); 580 lf.lfHeight = Globals.FontHeight; 581 582 cf.lStructSize = sizeof(cf); 583 cf.hwndOwner = Globals.hMainWnd; 584 cf.lpLogFont = &lf; 585 cf.Flags = CF_INITTOLOGFONTSTRUCT | CF_NOSTYLESEL; 586 587 if (!ChooseFontW(&cf)) 588 return; 589 590 hFont = CreateFontIndirectW(&lf); 591 592 if (!hFont) 593 return; 594 595 /* Set font information */ 596 StringCchCopyW(Globals.FontFaceName, _countof(Globals.FontFaceName), lf.lfFaceName); 597 Globals.FontHeight = lf.lfHeight; 598 599 hOldFont = Globals.hFont; 600 Globals.hFont = hFont; 601 602 for (i = 0; i < Globals.Keyboard->KeyCount; i++) 603 SendMessageW(Globals.hKeys[i], WM_SETFONT, (WPARAM)Globals.hFont, TRUE); 604 605 DeleteObject(hOldFont); 606 } 607 608 /*********************************************************************** 609 * 610 * OSK_Command 611 * 612 * All handling of commands 613 */ 614 BOOL OSK_Command(WPARAM wCommand, HWND hWndControl) 615 { 616 WORD ScanCode; 617 INPUT Input; 618 BOOL bExtendedKey; 619 BOOL bKeyDown; 620 BOOL bKeyUp; 621 LONG WindowStyle; 622 INT i; 623 624 /* KeyDown and/or KeyUp ? */ 625 WindowStyle = GetWindowLongW(hWndControl, GWL_STYLE); 626 if ((WindowStyle & BS_AUTOCHECKBOX) == BS_AUTOCHECKBOX) 627 { 628 /* 2-states key like Shift, Alt, Ctrl, ... */ 629 if (SendMessageW(hWndControl, BM_GETCHECK, 0, 0) == BST_CHECKED) 630 { 631 bKeyDown = TRUE; 632 bKeyUp = FALSE; 633 } 634 else 635 { 636 bKeyDown = FALSE; 637 bKeyUp = TRUE; 638 } 639 } 640 else 641 { 642 /* Other key */ 643 bKeyDown = TRUE; 644 bKeyUp = TRUE; 645 } 646 647 /* Get the key from dialog control key command */ 648 ScanCode = wCommand; 649 650 /* 651 The user could've pushed one of the key buttons of the dialog that 652 can trigger particular function toggling (Caps Lock, Num Lock or Scroll Lock). Update 653 (invalidate) the LED icon resources accordingly. 654 */ 655 for (i = 0; i < _countof(LedKey); i++) 656 { 657 if (LedKey[i].wScanCode == ScanCode) 658 { 659 InvalidateRect(GetDlgItem(Globals.hMainWnd, LedKey[i].DlgResource), NULL, FALSE); 660 } 661 } 662 663 /* Extended key ? */ 664 if (ScanCode & 0x0200) 665 bExtendedKey = TRUE; 666 else 667 bExtendedKey = FALSE; 668 ScanCode &= SCANCODE_MASK; 669 670 /* Press and release the key */ 671 if (bKeyDown) 672 { 673 Input.type = INPUT_KEYBOARD; 674 Input.ki.wVk = 0; 675 Input.ki.wScan = ScanCode; 676 Input.ki.time = GetTickCount(); 677 Input.ki.dwExtraInfo = GetMessageExtraInfo(); 678 Input.ki.dwFlags = KEYEVENTF_SCANCODE; 679 if (bExtendedKey) Input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; 680 SendInput(1, &Input, sizeof(Input)); 681 } 682 683 if (bKeyUp) 684 { 685 Input.type = INPUT_KEYBOARD; 686 Input.ki.wVk = 0; 687 Input.ki.wScan = ScanCode; 688 Input.ki.time = GetTickCount(); 689 Input.ki.dwExtraInfo = GetMessageExtraInfo(); 690 Input.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP; 691 if (bExtendedKey) Input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; 692 SendInput(1, &Input, sizeof(Input)); 693 } 694 695 /* Play the sound during clicking event (only if "Use Click Sound" menu option is ticked) */ 696 if (Globals.bSoundClick) 697 { 698 PlaySoundW(MAKEINTRESOURCEW(IDI_SOUNDCLICK), GetModuleHandle(NULL), SND_RESOURCE | SND_ASYNC); 699 } 700 701 return TRUE; 702 } 703 704 /*********************************************************************** 705 * 706 * OSK_ReleaseKey 707 * 708 * Release the key of ID wCommand 709 */ 710 BOOL OSK_ReleaseKey(WORD ScanCode) 711 { 712 INPUT Input; 713 BOOL bExtendedKey; 714 LONG WindowStyle; 715 HWND hWndControl; 716 717 /* Is it a 2-states key ? */ 718 hWndControl = GetDlgItem(Globals.hMainWnd, ScanCode); 719 WindowStyle = GetWindowLongW(hWndControl, GWL_STYLE); 720 if ((WindowStyle & BS_AUTOCHECKBOX) != BS_AUTOCHECKBOX) return FALSE; 721 722 /* Is the key down ? */ 723 if (SendMessageW(hWndControl, BM_GETCHECK, 0, 0) != BST_CHECKED) return TRUE; 724 725 /* Extended key ? */ 726 if (ScanCode & 0x0200) 727 bExtendedKey = TRUE; 728 else 729 bExtendedKey = FALSE; 730 ScanCode &= SCANCODE_MASK; 731 732 /* Release the key */ 733 Input.type = INPUT_KEYBOARD; 734 Input.ki.wVk = 0; 735 Input.ki.wScan = ScanCode; 736 Input.ki.time = GetTickCount(); 737 Input.ki.dwExtraInfo = GetMessageExtraInfo(); 738 Input.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP; 739 if (bExtendedKey) Input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; 740 SendInput(1, &Input, sizeof(Input)); 741 742 return TRUE; 743 } 744 745 /*********************************************************************** 746 * 747 * OSK_Paint 748 * 749 * Handles WM_PAINT messages 750 */ 751 LRESULT OSK_Paint(HWND hwnd) 752 { 753 PAINTSTRUCT ps; 754 RECT rcText; 755 HFONT hOldFont = NULL; 756 WCHAR szTemp[MAX_PATH]; 757 758 HDC hdc = BeginPaint(hwnd, &ps); 759 760 if (Globals.hFont) 761 hOldFont = SelectObject(hdc, Globals.hFont); 762 763 rcText.left = Globals.Keyboard->LedTextStart.x; 764 rcText.top = Globals.Keyboard->LedTextStart.y; 765 rcText.right = rcText.left + Globals.Keyboard->LedTextSize.cx; 766 rcText.bottom = rcText.top + Globals.Keyboard->LedTextSize.cy; 767 768 LoadStringW(Globals.hInstance, IDS_NUMLOCK, szTemp, _countof(szTemp)); 769 DrawTextW(hdc, szTemp, -1, &rcText, DT_NOCLIP); 770 771 OffsetRect(&rcText, Globals.Keyboard->LedTextOffset, 0); 772 773 LoadStringW(Globals.hInstance, IDS_CAPSLOCK, szTemp, _countof(szTemp)); 774 DrawTextW(hdc, szTemp, -1, &rcText, DT_NOCLIP); 775 776 OffsetRect(&rcText, Globals.Keyboard->LedTextOffset, 0); 777 778 LoadStringW(Globals.hInstance, IDS_SCROLLLOCK, szTemp, _countof(szTemp)); 779 DrawTextW(hdc, szTemp, -1, &rcText, DT_NOCLIP); 780 781 if (hOldFont) 782 SelectObject(hdc, hOldFont); 783 784 EndPaint(hwnd, &ps); 785 786 return 0; 787 } 788 /*********************************************************************** 789 * 790 * OSK_WndProc 791 */ 792 LRESULT APIENTRY OSK_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 793 { 794 switch (msg) 795 { 796 case WM_CREATE: 797 return OSK_Create(hwnd); 798 799 case WM_PAINT: 800 return OSK_Paint(hwnd); 801 802 case WM_TIMER: 803 return OSK_Timer(); 804 805 case WM_CTLCOLORSTATIC: 806 if ((HWND)lParam == GetDlgItem(hwnd, IDC_LED_NUM)) 807 { 808 if (GetKeyState(VK_NUMLOCK) & 0x0001) 809 return (LRESULT)Globals.hBrushGreenLed; 810 else 811 return (LRESULT)GetStockObject(BLACK_BRUSH); 812 } 813 if ((HWND)lParam == GetDlgItem(hwnd, IDC_LED_CAPS)) 814 { 815 if (GetKeyState(VK_CAPITAL) & 0x0001) 816 return (LRESULT)Globals.hBrushGreenLed; 817 else 818 return (LRESULT)GetStockObject(BLACK_BRUSH); 819 } 820 if ((HWND)lParam == GetDlgItem(hwnd, IDC_LED_SCROLL)) 821 { 822 if (GetKeyState(VK_SCROLL) & 0x0001) 823 return (LRESULT)Globals.hBrushGreenLed; 824 else 825 return (LRESULT)GetStockObject(BLACK_BRUSH); 826 } 827 break; 828 829 case WM_COMMAND: 830 switch (LOWORD(wParam)) 831 { 832 case IDM_EXIT: 833 { 834 PostMessageW(hwnd, WM_CLOSE, 0, 0); 835 break; 836 } 837 838 case IDM_ENHANCED_KB: 839 { 840 if (!Globals.bIsEnhancedKeyboard) 841 { 842 /* 843 The user attempted to switch to enhanced keyboard dialog type. 844 Set the member value as TRUE, destroy the dialog and save the data configuration into the registry. 845 */ 846 Globals.bIsEnhancedKeyboard = TRUE; 847 SaveSettings(); 848 849 /* Change the condition of enhanced keyboard item menu to checked */ 850 CheckMenuItem(GetMenu(hwnd), IDM_ENHANCED_KB, MF_BYCOMMAND | MF_CHECKED); 851 CheckMenuItem(GetMenu(hwnd), IDM_STANDARD_KB, MF_BYCOMMAND | MF_UNCHECKED); 852 853 /* Finally, update the key layout */ 854 LoadSettings(); 855 OSK_SetKeys(SETKEYS_LAYOUT); 856 } 857 858 break; 859 } 860 861 case IDM_STANDARD_KB: 862 { 863 if (Globals.bIsEnhancedKeyboard) 864 { 865 /* 866 The user attempted to switch to standard keyboard dialog type. 867 Set the member value as FALSE, destroy the dialog and save the data configuration into the registry. 868 */ 869 Globals.bIsEnhancedKeyboard = FALSE; 870 SaveSettings(); 871 872 /* Change the condition of standard keyboard item menu to checked */ 873 CheckMenuItem(GetMenu(hwnd), IDM_ENHANCED_KB, MF_BYCOMMAND | MF_UNCHECKED); 874 CheckMenuItem(GetMenu(hwnd), IDM_STANDARD_KB, MF_BYCOMMAND | MF_CHECKED); 875 876 /* Finally, update the key layout */ 877 LoadSettings(); 878 OSK_SetKeys(SETKEYS_LAYOUT); 879 } 880 881 break; 882 } 883 884 case IDM_CLICK_SOUND: 885 { 886 /* 887 This case is triggered when the user attempts to click on the menu item. Before doing anything, 888 we must check the condition state of such menu item so that we can tick/untick the menu item accordingly. 889 */ 890 if (!Globals.bSoundClick) 891 { 892 Globals.bSoundClick = TRUE; 893 CheckMenuItem(GetMenu(hwnd), IDM_CLICK_SOUND, MF_BYCOMMAND | MF_CHECKED); 894 } 895 else 896 { 897 Globals.bSoundClick = FALSE; 898 CheckMenuItem(GetMenu(hwnd), IDM_CLICK_SOUND, MF_BYCOMMAND | MF_UNCHECKED); 899 } 900 901 break; 902 } 903 904 case IDM_ON_TOP: 905 { 906 /* 907 Check the condition state before disabling/enabling the menu 908 item and change the topmost order. 909 */ 910 if (!Globals.bAlwaysOnTop) 911 { 912 Globals.bAlwaysOnTop = TRUE; 913 CheckMenuItem(GetMenu(hwnd), IDM_ON_TOP, MF_BYCOMMAND | MF_CHECKED); 914 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); 915 } 916 else 917 { 918 Globals.bAlwaysOnTop = FALSE; 919 CheckMenuItem(GetMenu(hwnd), IDM_ON_TOP, MF_BYCOMMAND | MF_UNCHECKED); 920 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); 921 } 922 923 break; 924 } 925 926 case IDM_FONT: 927 { 928 OSK_ChooseFont(); 929 break; 930 } 931 932 case IDM_ABOUT: 933 { 934 OSK_About(); 935 break; 936 } 937 938 default: 939 OSK_Command(wParam, (HWND)lParam); 940 break; 941 } 942 return 0; 943 944 case WM_THEMECHANGED: 945 /* Redraw the dialog (and its control buttons) using the new theme */ 946 InvalidateRect(hwnd, NULL, FALSE); 947 return 0; 948 949 case WM_CLOSE: 950 OSK_Close(); 951 PostQuitMessage(0); 952 return 0; 953 } 954 return DefWindowProcW(hwnd, msg, wParam, lParam); 955 } 956 957 /*********************************************************************** 958 * 959 * WinMain 960 */ 961 int WINAPI wWinMain(HINSTANCE hInstance, 962 HINSTANCE prev, 963 LPWSTR cmdline, 964 int show) 965 { 966 DWORD dwError; 967 HANDLE hMutex; 968 INITCOMMONCONTROLSEX iccex; 969 WNDCLASSEXW wc = {0}; 970 MSG msg; 971 HWND hwnd; 972 973 UNREFERENCED_PARAMETER(prev); 974 UNREFERENCED_PARAMETER(cmdline); 975 UNREFERENCED_PARAMETER(show); 976 977 /* 978 Obtain a mutex for the program. This will ensure that 979 the program is launched only once. 980 */ 981 hMutex = CreateMutexW(NULL, FALSE, L"OSKRunning"); 982 983 if (hMutex) 984 { 985 /* Check if there's already a mutex for the program */ 986 dwError = GetLastError(); 987 988 if (dwError == ERROR_ALREADY_EXISTS) 989 { 990 /* 991 A mutex with the object name has been created previously. 992 Therefore, another instance is already running. 993 */ 994 DPRINT("wWinMain(): Failed to create a mutex! The program instance is already running.\n"); 995 CloseHandle(hMutex); 996 return 0; 997 } 998 } 999 1000 /* Load the common controls */ 1001 iccex.dwSize = sizeof(INITCOMMONCONTROLSEX); 1002 iccex.dwICC = ICC_STANDARD_CLASSES | ICC_WIN95_CLASSES; 1003 InitCommonControlsEx(&iccex); 1004 1005 ZeroMemory(&Globals, sizeof(Globals)); 1006 Globals.hInstance = hInstance; 1007 1008 /* Load the application's settings from the registry */ 1009 LoadSettings(); 1010 1011 /* Define the window class */ 1012 wc.cbSize = sizeof(wc); 1013 wc.hInstance = Globals.hInstance; 1014 wc.lpfnWndProc = OSK_WndProc; 1015 wc.lpszMenuName = MAKEINTRESOURCEW(IDR_OSK_MENU); 1016 wc.lpszClassName = OSK_CLASS; 1017 wc.style = CS_HREDRAW | CS_VREDRAW; 1018 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 1019 /* Set the application's icon */ 1020 wc.hIcon = LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IDI_OSK), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE); 1021 wc.hIconSm = CopyImage(wc.hIcon, IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_COPYFROMRESOURCE); 1022 1023 if (!RegisterClassExW(&wc)) 1024 goto quit; 1025 1026 /* Load window title */ 1027 LoadStringW(Globals.hInstance, IDS_OSK, Globals.szTitle, _countof(Globals.szTitle)); 1028 1029 hwnd = CreateWindowExW(WS_EX_TOPMOST | WS_EX_APPWINDOW | WS_EX_NOACTIVATE, 1030 OSK_CLASS, 1031 Globals.szTitle, 1032 WS_SYSMENU | WS_MINIMIZEBOX, 1033 CW_USEDEFAULT, 1034 CW_USEDEFAULT, 1035 CW_USEDEFAULT, 1036 CW_USEDEFAULT, 1037 NULL, 1038 NULL, 1039 Globals.hInstance, 1040 NULL); 1041 1042 if (!hwnd) 1043 goto quit; 1044 1045 ShowWindow(hwnd, SW_SHOW); 1046 UpdateWindow(hwnd); 1047 1048 while (GetMessageW(&msg, NULL, 0, 0)) 1049 { 1050 TranslateMessage(&msg); 1051 DispatchMessageW(&msg); 1052 } 1053 1054 quit: 1055 /* Delete the mutex */ 1056 if (hMutex) 1057 { 1058 CloseHandle(hMutex); 1059 } 1060 1061 return 0; 1062 } 1063 1064 /* EOF */ 1065