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