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 */ 8 9 /* INCLUDES *******************************************************************/ 10 11 #include "precomp.h" 12 13 /* GLOBALS ********************************************************************/ 14 15 OSK_GLOBALS Globals; 16 17 OSK_KEYLEDINDICATOR LedKey[] = 18 { 19 {VK_NUMLOCK, IDC_LED_NUM, 0x0145, FALSE}, 20 {VK_CAPITAL, IDC_LED_CAPS, 0x013A, FALSE}, 21 {VK_SCROLL, IDC_LED_SCROLL, 0x0146, FALSE} 22 }; 23 24 /* FUNCTIONS ******************************************************************/ 25 26 /*********************************************************************** 27 * 28 * OSK_SetImage 29 * 30 * Set an image on a button 31 */ 32 int OSK_SetImage(int IdDlgItem, int IdResource) 33 { 34 HICON hIcon; 35 HWND hWndItem; 36 37 hIcon = (HICON)LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IdResource), 38 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR); 39 if (hIcon == NULL) 40 return FALSE; 41 42 hWndItem = GetDlgItem(Globals.hMainWnd, IdDlgItem); 43 if (hWndItem == NULL) 44 { 45 DestroyIcon(hIcon); 46 return FALSE; 47 } 48 49 SendMessageW(hWndItem, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hIcon); 50 51 /* The system automatically deletes these resources when the process that created them terminates (MSDN) */ 52 53 return TRUE; 54 } 55 56 /*********************************************************************** 57 * 58 * OSK_WarningProc 59 * 60 * Function handler for the warning dialog box on startup 61 */ 62 INT_PTR CALLBACK OSK_WarningProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam) 63 { 64 UNREFERENCED_PARAMETER(lParam); 65 66 switch (Msg) 67 { 68 case WM_INITDIALOG: 69 { 70 return TRUE; 71 } 72 73 case WM_COMMAND: 74 { 75 switch (LOWORD(wParam)) 76 { 77 case IDC_SHOWWARNINGCHECK: 78 { 79 Globals.bShowWarning = !IsDlgButtonChecked(hDlg, IDC_SHOWWARNINGCHECK); 80 return TRUE; 81 } 82 83 case IDOK: 84 case IDCANCEL: 85 { 86 EndDialog(hDlg, LOWORD(wParam)); 87 return TRUE; 88 } 89 } 90 break; 91 } 92 } 93 94 return FALSE; 95 } 96 97 /*********************************************************************** 98 * 99 * OSK_WarningDlgThread 100 * 101 * Thread procedure routine for the warning dialog box 102 */ 103 DWORD WINAPI OSK_WarningDlgThread(LPVOID lpParameter) 104 { 105 HINSTANCE hInstance = (HINSTANCE)lpParameter; 106 107 DialogBoxW(hInstance, MAKEINTRESOURCEW(IDD_WARNINGDIALOG_OSK), Globals.hMainWnd, OSK_WarningProc); 108 return 0; 109 } 110 111 /*********************************************************************** 112 * 113 * OSK_About 114 * 115 * Initializes the "About" dialog box 116 */ 117 VOID OSK_About(VOID) 118 { 119 WCHAR szTitle[MAX_BUFF]; 120 WCHAR szAuthors[MAX_BUFF]; 121 HICON OSKIcon; 122 123 /* Load the icon */ 124 OSKIcon = LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IDI_OSK), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE); 125 126 /* Load the strings into the "About" dialog */ 127 LoadStringW(Globals.hInstance, STRING_OSK, szTitle, countof(szTitle)); 128 LoadStringW(Globals.hInstance, STRING_AUTHORS, szAuthors, countof(szAuthors)); 129 130 /* Finally, execute the "About" dialog by using the Shell routine */ 131 ShellAboutW(Globals.hMainWnd, szTitle, szAuthors, OSKIcon); 132 133 /* Once done, destroy the icon */ 134 DestroyIcon(OSKIcon); 135 } 136 137 138 /*********************************************************************** 139 * 140 * OSK_DlgInitDialog 141 * 142 * Handling of WM_INITDIALOG 143 */ 144 int OSK_DlgInitDialog(HWND hDlg) 145 { 146 HICON hIcon, hIconSm; 147 HMONITOR monitor; 148 MONITORINFO info; 149 POINT Pt; 150 RECT rcWindow, rcDlgIntersect; 151 152 /* Save handle */ 153 Globals.hMainWnd = hDlg; 154 155 /* Check the checked menu item before displaying the modal box */ 156 if (Globals.bIsEnhancedKeyboard) 157 { 158 /* Enhanced keyboard dialog chosen, set the respective menu item as checked */ 159 CheckMenuItem(GetMenu(hDlg), IDM_ENHANCED_KB, MF_BYCOMMAND | MF_CHECKED); 160 CheckMenuItem(GetMenu(hDlg), IDM_STANDARD_KB, MF_BYCOMMAND | MF_UNCHECKED); 161 } 162 else 163 { 164 /* Standard keyboard dialog chosen, set the respective menu item as checked */ 165 CheckMenuItem(GetMenu(hDlg), IDM_STANDARD_KB, MF_BYCOMMAND | MF_CHECKED); 166 CheckMenuItem(GetMenu(hDlg), IDM_ENHANCED_KB, MF_BYCOMMAND | MF_UNCHECKED); 167 } 168 169 /* Check if the "Click Sound" option was chosen before (and if so, then tick the menu item) */ 170 if (Globals.bSoundClick) 171 { 172 CheckMenuItem(GetMenu(hDlg), IDM_CLICK_SOUND, MF_BYCOMMAND | MF_CHECKED); 173 } 174 175 /* Set the application's icon */ 176 hIcon = LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IDI_OSK), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE); 177 hIconSm = CopyImage(hIcon, IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_COPYFROMRESOURCE); 178 if (hIcon || hIconSm) 179 { 180 /* Set the window icons (they are deleted when the process terminates) */ 181 SendMessageW(Globals.hMainWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon); 182 SendMessageW(Globals.hMainWnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm); 183 } 184 185 /* Get screen info */ 186 memset(&Pt, 0, sizeof(Pt)); 187 monitor = MonitorFromPoint(Pt, MONITOR_DEFAULTTOPRIMARY); 188 info.cbSize = sizeof(info); 189 GetMonitorInfoW(monitor, &info); 190 GetWindowRect(hDlg, &rcWindow); 191 192 /* 193 If the coordination values are default then re-initialize using the specific formulas 194 to move the dialog at the bottom of the screen. 195 */ 196 if (Globals.PosX == CW_USEDEFAULT && Globals.PosY == CW_USEDEFAULT) 197 { 198 Globals.PosX = (info.rcMonitor.left + info.rcMonitor.right - (rcWindow.right - rcWindow.left)) / 2; 199 Globals.PosY = info.rcMonitor.bottom - (rcWindow.bottom - rcWindow.top); 200 } 201 202 /* 203 Calculate the intersection of two rectangle sources (dialog and work desktop area). 204 If such sources do not intersect, then the dialog is deemed as "off screen". 205 */ 206 if (IntersectRect(&rcDlgIntersect, &rcWindow, &info.rcWork) == 0) 207 { 208 Globals.PosX = (info.rcMonitor.left + info.rcMonitor.right - (rcWindow.right - rcWindow.left)) / 2; 209 Globals.PosY = info.rcMonitor.bottom - (rcWindow.bottom - rcWindow.top); 210 } 211 else 212 { 213 /* 214 There's still some intersection but we're not for sure if it is sufficient (the dialog could also be partially hidden). 215 Therefore, check the remaining intersection if it's enough. 216 */ 217 if (rcWindow.top < info.rcWork.top || rcWindow.left < info.rcWork.left || rcWindow.right > info.rcWork.right || rcWindow.bottom > info.rcWork.bottom) 218 { 219 Globals.PosX = (info.rcMonitor.left + info.rcMonitor.right - (rcWindow.right - rcWindow.left)) / 2; 220 Globals.PosY = info.rcMonitor.bottom - (rcWindow.bottom - rcWindow.top); 221 } 222 } 223 224 /* 225 Place the window (with respective placement coordinates) as topmost, above 226 every window which are not on top or are at the bottom of the Z order. 227 */ 228 if (Globals.bAlwaysOnTop) 229 { 230 CheckMenuItem(GetMenu(hDlg), IDM_ON_TOP, MF_BYCOMMAND | MF_CHECKED); 231 SetWindowPos(hDlg, HWND_TOPMOST, Globals.PosX, Globals.PosY, 0, 0, SWP_NOSIZE); 232 } 233 else 234 { 235 CheckMenuItem(GetMenu(hDlg), IDM_ON_TOP, MF_BYCOMMAND | MF_UNCHECKED); 236 SetWindowPos(hDlg, HWND_NOTOPMOST, Globals.PosX, Globals.PosY, 0, 0, SWP_NOSIZE); 237 } 238 239 /* Set icon on visual buttons */ 240 OSK_SetImage(SCAN_CODE_15, IDI_BACK); 241 OSK_SetImage(SCAN_CODE_16, IDI_TAB); 242 OSK_SetImage(SCAN_CODE_30, IDI_CAPS_LOCK); 243 OSK_SetImage(SCAN_CODE_43, IDI_RETURN); 244 OSK_SetImage(SCAN_CODE_44, IDI_SHIFT); 245 OSK_SetImage(SCAN_CODE_57, IDI_SHIFT); 246 OSK_SetImage(SCAN_CODE_127, IDI_REACTOS); 247 OSK_SetImage(SCAN_CODE_128, IDI_REACTOS); 248 OSK_SetImage(SCAN_CODE_129, IDI_MENU); 249 OSK_SetImage(SCAN_CODE_80, IDI_HOME); 250 OSK_SetImage(SCAN_CODE_85, IDI_PG_UP); 251 OSK_SetImage(SCAN_CODE_86, IDI_PG_DOWN); 252 OSK_SetImage(SCAN_CODE_79, IDI_LEFT); 253 OSK_SetImage(SCAN_CODE_83, IDI_TOP); 254 OSK_SetImage(SCAN_CODE_84, IDI_BOTTOM); 255 OSK_SetImage(SCAN_CODE_89, IDI_RIGHT); 256 257 /* Create a green brush for leds */ 258 Globals.hBrushGreenLed = CreateSolidBrush(RGB(0, 255, 0)); 259 260 /* Set a timer for periodics tasks */ 261 Globals.iTimer = SetTimer(hDlg, 0, 50, NULL); 262 263 return TRUE; 264 } 265 266 /*********************************************************************** 267 * 268 * OSK_RestoreDlgPlacement 269 * 270 * Restores the dialog placement 271 */ 272 VOID OSK_RestoreDlgPlacement(HWND hDlg) 273 { 274 LoadSettings(); 275 SetWindowPos(hDlg, (Globals.bAlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST), Globals.PosX, Globals.PosY, 0, 0, SWP_NOSIZE); 276 } 277 278 /*********************************************************************** 279 * 280 * OSK_DlgClose 281 * 282 * Handling of WM_CLOSE 283 */ 284 int OSK_DlgClose(void) 285 { 286 KillTimer(Globals.hMainWnd, Globals.iTimer); 287 288 /* Release Ctrl, Shift, Alt keys */ 289 OSK_ReleaseKey(SCAN_CODE_44); // Left shift 290 OSK_ReleaseKey(SCAN_CODE_57); // Right shift 291 OSK_ReleaseKey(SCAN_CODE_58); // Left ctrl 292 OSK_ReleaseKey(SCAN_CODE_60); // Left alt 293 OSK_ReleaseKey(SCAN_CODE_62); // Right alt 294 OSK_ReleaseKey(SCAN_CODE_64); // Right ctrl 295 296 /* delete GDI objects */ 297 if (Globals.hBrushGreenLed) DeleteObject(Globals.hBrushGreenLed); 298 299 /* Save the application's settings on registry */ 300 SaveSettings(); 301 302 return TRUE; 303 } 304 305 /*********************************************************************** 306 * 307 * OSK_RefreshLEDKeys 308 * 309 * Updates (invalidates) the LED icon resources then the respective 310 * keys (Caps Lock, Scroll Lock or Num Lock) are being held down 311 */ 312 VOID OSK_RefreshLEDKeys(VOID) 313 { 314 INT i; 315 BOOL bKeyIsPressed; 316 317 for (i = 0; i < _countof(LedKey); i++) 318 { 319 bKeyIsPressed = (GetAsyncKeyState(LedKey[i].vKey) & 0x8000) != 0; 320 if (LedKey[i].bWasKeyPressed != bKeyIsPressed) 321 { 322 LedKey[i].bWasKeyPressed = bKeyIsPressed; 323 InvalidateRect(GetDlgItem(Globals.hMainWnd, LedKey[i].DlgResource), NULL, FALSE); 324 } 325 } 326 } 327 328 /*********************************************************************** 329 * 330 * OSK_DlgTimer 331 * 332 * Handling of WM_TIMER 333 */ 334 int OSK_DlgTimer(void) 335 { 336 /* FIXME: To be deleted when ReactOS will support WS_EX_NOACTIVATE */ 337 HWND hWndActiveWindow; 338 339 hWndActiveWindow = GetForegroundWindow(); 340 if (hWndActiveWindow != NULL && hWndActiveWindow != Globals.hMainWnd) 341 { 342 Globals.hActiveWnd = hWndActiveWindow; 343 } 344 345 /* 346 Update the LED key indicators accordingly to their state (if one 347 of the specific keys is held down). 348 */ 349 OSK_RefreshLEDKeys(); 350 351 return TRUE; 352 } 353 354 /*********************************************************************** 355 * 356 * OSK_DlgCommand 357 * 358 * All handling of dialog command 359 */ 360 BOOL OSK_DlgCommand(WPARAM wCommand, HWND hWndControl) 361 { 362 WORD ScanCode; 363 INPUT Input; 364 BOOL bExtendedKey; 365 BOOL bKeyDown; 366 BOOL bKeyUp; 367 LONG WindowStyle; 368 INT i; 369 370 /* FIXME: To be deleted when ReactOS will support WS_EX_NOACTIVATE */ 371 if (Globals.hActiveWnd) 372 { 373 MSG msg; 374 375 SetForegroundWindow(Globals.hActiveWnd); 376 while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) 377 { 378 TranslateMessage(&msg); 379 DispatchMessageW(&msg); 380 } 381 } 382 383 /* KeyDown and/or KeyUp ? */ 384 WindowStyle = GetWindowLongW(hWndControl, GWL_STYLE); 385 if ((WindowStyle & BS_AUTOCHECKBOX) == BS_AUTOCHECKBOX) 386 { 387 /* 2-states key like Shift, Alt, Ctrl, ... */ 388 if (SendMessageW(hWndControl, BM_GETCHECK, 0, 0) == BST_CHECKED) 389 { 390 bKeyDown = TRUE; 391 bKeyUp = FALSE; 392 } 393 else 394 { 395 bKeyDown = FALSE; 396 bKeyUp = TRUE; 397 } 398 } 399 else 400 { 401 /* Other key */ 402 bKeyDown = TRUE; 403 bKeyUp = TRUE; 404 } 405 406 /* Get the key from dialog control key command */ 407 ScanCode = wCommand; 408 409 /* 410 The user could've pushed one of the key buttons of the dialog that 411 can trigger particular function toggling (Caps Lock, Num Lock or Scroll Lock). Update 412 (invalidate) the LED icon resources accordingly. 413 */ 414 for (i = 0; i < _countof(LedKey); i++) 415 { 416 if (LedKey[i].wScanCode == ScanCode) 417 { 418 InvalidateRect(GetDlgItem(Globals.hMainWnd, LedKey[i].DlgResource), NULL, FALSE); 419 } 420 } 421 422 /* Extended key ? */ 423 if (ScanCode & 0x0200) 424 bExtendedKey = TRUE; 425 else 426 bExtendedKey = FALSE; 427 ScanCode &= 0xFF; 428 429 /* Press and release the key */ 430 if (bKeyDown) 431 { 432 Input.type = INPUT_KEYBOARD; 433 Input.ki.wVk = 0; 434 Input.ki.wScan = ScanCode; 435 Input.ki.time = GetTickCount(); 436 Input.ki.dwExtraInfo = GetMessageExtraInfo(); 437 Input.ki.dwFlags = KEYEVENTF_SCANCODE; 438 if (bExtendedKey) Input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; 439 SendInput(1, &Input, sizeof(Input)); 440 } 441 442 if (bKeyUp) 443 { 444 Input.type = INPUT_KEYBOARD; 445 Input.ki.wVk = 0; 446 Input.ki.wScan = ScanCode; 447 Input.ki.time = GetTickCount(); 448 Input.ki.dwExtraInfo = GetMessageExtraInfo(); 449 Input.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP; 450 if (bExtendedKey) Input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; 451 SendInput(1, &Input, sizeof(Input)); 452 } 453 454 /* Play the sound during clicking event (only if "Use Click Sound" menu option is ticked) */ 455 if (Globals.bSoundClick) 456 { 457 PlaySoundW(MAKEINTRESOURCEW(IDI_SOUNDCLICK), GetModuleHandle(NULL), SND_RESOURCE | SND_ASYNC); 458 } 459 460 return TRUE; 461 } 462 463 /*********************************************************************** 464 * 465 * OSK_ReleaseKey 466 * 467 * Release the key of ID wCommand 468 */ 469 BOOL OSK_ReleaseKey(WORD ScanCode) 470 { 471 INPUT Input; 472 BOOL bExtendedKey; 473 LONG WindowStyle; 474 HWND hWndControl; 475 476 /* Is it a 2-states key ? */ 477 hWndControl = GetDlgItem(Globals.hMainWnd, ScanCode); 478 WindowStyle = GetWindowLongW(hWndControl, GWL_STYLE); 479 if ((WindowStyle & BS_AUTOCHECKBOX) != BS_AUTOCHECKBOX) return FALSE; 480 481 /* Is the key down ? */ 482 if (SendMessageW(hWndControl, BM_GETCHECK, 0, 0) != BST_CHECKED) return TRUE; 483 484 /* Extended key ? */ 485 if (ScanCode & 0x0200) 486 bExtendedKey = TRUE; 487 else 488 bExtendedKey = FALSE; 489 ScanCode &= 0xFF; 490 491 /* Release the key */ 492 Input.type = INPUT_KEYBOARD; 493 Input.ki.wVk = 0; 494 Input.ki.wScan = ScanCode; 495 Input.ki.time = GetTickCount(); 496 Input.ki.dwExtraInfo = GetMessageExtraInfo(); 497 Input.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP; 498 if (bExtendedKey) Input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; 499 SendInput(1, &Input, sizeof(Input)); 500 501 return TRUE; 502 } 503 504 /*********************************************************************** 505 * 506 * OSK_ThemeHandler 507 * 508 * Function helper which handles theme drawing of controls 509 */ 510 LRESULT APIENTRY OSK_ThemeHandler(HWND hDlg, NMCUSTOMDRAW *pNmDraw) 511 { 512 HTHEME hTheme; 513 HWND hDlgButtonCtrl; 514 LRESULT Ret; 515 INT iState = PBS_NORMAL; 516 517 /* Retrieve the theme handle for the button controls */ 518 hDlgButtonCtrl = pNmDraw->hdr.hwndFrom; 519 hTheme = GetWindowTheme(hDlgButtonCtrl); 520 521 /* 522 Begin the painting procedures if we retrieved 523 the theme for control buttons of the dialog. 524 */ 525 if (hTheme) 526 { 527 /* Obtain CDDS drawing stages */ 528 switch (pNmDraw->dwDrawStage) 529 { 530 case CDDS_PREPAINT: 531 { 532 /* 533 The button could be either in normal state or pushed. 534 Retrieve its state and save to a variable. 535 */ 536 if (pNmDraw->uItemState & CDIS_DEFAULT) 537 { 538 iState = PBS_DEFAULTED; 539 } 540 else if (pNmDraw->uItemState & CDIS_SELECTED) 541 { 542 iState = PBS_PRESSED; 543 } 544 else if (pNmDraw->uItemState & CDIS_HOT) 545 { 546 iState = PBS_HOT; 547 } 548 549 if (IsThemeBackgroundPartiallyTransparent(hTheme, BP_PUSHBUTTON, iState)) 550 { 551 /* Draw the application if the theme is transparent */ 552 DrawThemeParentBackground(hDlgButtonCtrl, pNmDraw->hdc, &pNmDraw->rc); 553 } 554 555 /* Draw it */ 556 DrawThemeBackground(hTheme, pNmDraw->hdc, BP_PUSHBUTTON, iState, &pNmDraw->rc, NULL); 557 558 Ret = CDRF_SKIPDEFAULT; 559 break; 560 } 561 562 case CDDS_PREERASE: 563 { 564 Ret = CDRF_DODEFAULT; 565 break; 566 } 567 568 default: 569 Ret = CDRF_SKIPDEFAULT; 570 break; 571 } 572 } 573 else 574 { 575 /* hTheme is NULL so bail right away */ 576 Ret = CDRF_DODEFAULT; 577 } 578 579 return Ret; 580 } 581 582 /*********************************************************************** 583 * 584 * OSK_DlgProc 585 */ 586 INT_PTR APIENTRY OSK_DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) 587 { 588 switch (msg) 589 { 590 case WM_INITDIALOG: 591 OSK_DlgInitDialog(hDlg); 592 return TRUE; 593 594 case WM_TIMER: 595 OSK_DlgTimer(); 596 return TRUE; 597 598 case WM_NOTIFY: 599 return OSK_ThemeHandler(hDlg, (LPNMCUSTOMDRAW)lParam); 600 601 case WM_CTLCOLORSTATIC: 602 if ((HWND)lParam == GetDlgItem(hDlg, IDC_LED_NUM)) 603 { 604 if (GetKeyState(VK_NUMLOCK) & 0x0001) 605 return (INT_PTR)Globals.hBrushGreenLed; 606 else 607 return (INT_PTR)GetStockObject(BLACK_BRUSH); 608 } 609 if ((HWND)lParam == GetDlgItem(hDlg, IDC_LED_CAPS)) 610 { 611 if (GetKeyState(VK_CAPITAL) & 0x0001) 612 return (INT_PTR)Globals.hBrushGreenLed; 613 else 614 return (INT_PTR)GetStockObject(BLACK_BRUSH); 615 } 616 if ((HWND)lParam == GetDlgItem(hDlg, IDC_LED_SCROLL)) 617 { 618 if (GetKeyState(VK_SCROLL) & 0x0001) 619 return (INT_PTR)Globals.hBrushGreenLed; 620 else 621 return (INT_PTR)GetStockObject(BLACK_BRUSH); 622 } 623 break; 624 625 case WM_COMMAND: 626 switch (LOWORD(wParam)) 627 { 628 case IDCANCEL: 629 { 630 EndDialog(hDlg, FALSE); 631 break; 632 } 633 634 case IDM_EXIT: 635 { 636 EndDialog(hDlg, FALSE); 637 break; 638 } 639 640 case IDM_ENHANCED_KB: 641 { 642 if (!Globals.bIsEnhancedKeyboard) 643 { 644 /* 645 The user attempted to switch to enhanced keyboard dialog type. 646 Set the member value as TRUE, destroy the dialog and save the data configuration into the registry. 647 */ 648 Globals.bIsEnhancedKeyboard = TRUE; 649 EndDialog(hDlg, FALSE); 650 SaveSettings(); 651 652 /* Change the condition of enhanced keyboard item menu to checked */ 653 CheckMenuItem(GetMenu(hDlg), IDM_ENHANCED_KB, MF_BYCOMMAND | MF_CHECKED); 654 CheckMenuItem(GetMenu(hDlg), IDM_STANDARD_KB, MF_BYCOMMAND | MF_UNCHECKED); 655 656 /* 657 Before creating the dialog box restore the coordinates. The user can 658 move the dialog around before choosing a different dialog layout therefore 659 we must create the dialog with the new coordinates. 660 */ 661 OSK_RestoreDlgPlacement(hDlg); 662 663 /* Finally, display the dialog modal box with the enhanced keyboard dialog */ 664 DialogBoxW(Globals.hInstance, 665 MAKEINTRESOURCEW(MAIN_DIALOG_ENHANCED_KB), 666 GetDesktopWindow(), 667 OSK_DlgProc); 668 } 669 670 break; 671 } 672 673 case IDM_STANDARD_KB: 674 { 675 if (Globals.bIsEnhancedKeyboard) 676 { 677 /* 678 The user attempted to switch to standard keyboard dialog type. 679 Set the member value as FALSE, destroy the dialog and save the data configuration into the registry. 680 */ 681 Globals.bIsEnhancedKeyboard = FALSE; 682 EndDialog(hDlg, FALSE); 683 SaveSettings(); 684 685 /* Change the condition of standard keyboard item menu to checked */ 686 CheckMenuItem(GetMenu(hDlg), IDM_ENHANCED_KB, MF_BYCOMMAND | MF_UNCHECKED); 687 CheckMenuItem(GetMenu(hDlg), IDM_STANDARD_KB, MF_BYCOMMAND | MF_CHECKED); 688 689 /* 690 Before creating the dialog box restore the coordinates. The user can 691 move the dialog around before choosing a different dialog layout therefore 692 we must create the dialog with the new coordinates. 693 */ 694 OSK_RestoreDlgPlacement(hDlg); 695 696 /* Finally, display the dialog modal box with the standard keyboard dialog */ 697 DialogBoxW(Globals.hInstance, 698 MAKEINTRESOURCEW(MAIN_DIALOG_STANDARD_KB), 699 GetDesktopWindow(), 700 OSK_DlgProc); 701 } 702 703 break; 704 } 705 706 case IDM_CLICK_SOUND: 707 { 708 /* 709 This case is triggered when the user attempts to click on the menu item. Before doing anything, 710 we must check the condition state of such menu item so that we can tick/untick the menu item accordingly. 711 */ 712 if (!Globals.bSoundClick) 713 { 714 Globals.bSoundClick = TRUE; 715 CheckMenuItem(GetMenu(hDlg), IDM_CLICK_SOUND, MF_BYCOMMAND | MF_CHECKED); 716 } 717 else 718 { 719 Globals.bSoundClick = FALSE; 720 CheckMenuItem(GetMenu(hDlg), IDM_CLICK_SOUND, MF_BYCOMMAND | MF_UNCHECKED); 721 } 722 723 break; 724 } 725 726 case IDM_ON_TOP: 727 { 728 /* 729 Check the condition state before disabling/enabling the menu 730 item and change the topmost order. 731 */ 732 if (!Globals.bAlwaysOnTop) 733 { 734 Globals.bAlwaysOnTop = TRUE; 735 CheckMenuItem(GetMenu(hDlg), IDM_ON_TOP, MF_BYCOMMAND | MF_CHECKED); 736 SetWindowPos(hDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); 737 } 738 else 739 { 740 Globals.bAlwaysOnTop = FALSE; 741 CheckMenuItem(GetMenu(hDlg), IDM_ON_TOP, MF_BYCOMMAND | MF_UNCHECKED); 742 SetWindowPos(hDlg, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); 743 } 744 745 break; 746 } 747 748 case IDM_ABOUT: 749 { 750 OSK_About(); 751 break; 752 } 753 754 default: 755 OSK_DlgCommand(wParam, (HWND)lParam); 756 break; 757 } 758 break; 759 760 case WM_THEMECHANGED: 761 /* Redraw the dialog (and its control buttons) using the new theme */ 762 InvalidateRect(hDlg, NULL, FALSE); 763 break; 764 765 case WM_CLOSE: 766 OSK_DlgClose(); 767 break; 768 } 769 770 return 0; 771 } 772 773 /*********************************************************************** 774 * 775 * WinMain 776 */ 777 int WINAPI wWinMain(HINSTANCE hInstance, 778 HINSTANCE prev, 779 LPWSTR cmdline, 780 int show) 781 { 782 HANDLE hMutex; 783 DWORD dwError; 784 INT LayoutResource; 785 INITCOMMONCONTROLSEX iccex; 786 787 UNREFERENCED_PARAMETER(prev); 788 UNREFERENCED_PARAMETER(cmdline); 789 UNREFERENCED_PARAMETER(show); 790 791 /* 792 Obtain a mutex for the program. This will ensure that 793 the program is launched only once. 794 */ 795 hMutex = CreateMutexW(NULL, FALSE, L"OSKRunning"); 796 797 if (hMutex) 798 { 799 /* Check if there's already a mutex for the program */ 800 dwError = GetLastError(); 801 802 if (dwError == ERROR_ALREADY_EXISTS) 803 { 804 /* 805 A mutex with the object name has been created previously. 806 Therefore, another instance is already running. 807 */ 808 DPRINT("wWinMain(): Failed to create a mutex! The program instance is already running.\n"); 809 CloseHandle(hMutex); 810 return 0; 811 } 812 } 813 814 /* Load the common controls */ 815 iccex.dwSize = sizeof(INITCOMMONCONTROLSEX); 816 iccex.dwICC = ICC_STANDARD_CLASSES | ICC_WIN95_CLASSES; 817 InitCommonControlsEx(&iccex); 818 819 ZeroMemory(&Globals, sizeof(Globals)); 820 Globals.hInstance = hInstance; 821 822 /* Load the application's settings from the registry */ 823 LoadSettings(); 824 825 /* If the member of the struct (bShowWarning) is set then display the dialog box */ 826 if (Globals.bShowWarning) 827 { 828 /* If for whatever reason the thread fails to be created then handle the dialog box in main thread... */ 829 if (CreateThread(NULL, 0, OSK_WarningDlgThread, (PVOID)Globals.hInstance, 0, NULL) == NULL) 830 { 831 DialogBoxW(Globals.hInstance, MAKEINTRESOURCEW(IDD_WARNINGDIALOG_OSK), Globals.hMainWnd, OSK_WarningProc); 832 } 833 } 834 835 /* Before initializing the dialog execution, check if the chosen keyboard type is standard or enhanced */ 836 if (Globals.bIsEnhancedKeyboard) 837 { 838 LayoutResource = MAIN_DIALOG_ENHANCED_KB; 839 } 840 else 841 { 842 LayoutResource = MAIN_DIALOG_STANDARD_KB; 843 } 844 845 /* Create the modal box based on the configuration registry */ 846 DialogBoxW(hInstance, 847 MAKEINTRESOURCEW(LayoutResource), 848 GetDesktopWindow(), 849 OSK_DlgProc); 850 851 /* Delete the mutex */ 852 if (hMutex) 853 { 854 CloseHandle(hMutex); 855 } 856 857 return 0; 858 } 859 860 /* EOF */ 861