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