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