1 /* 2 * PROJECT: ReactOS On-Screen Keyboard 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: base/applications/osk/main.c 5 * PURPOSE: On-screen keyboard. 6 * PROGRAMMERS: Denis ROBERT 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 26 INT_PTR APIENTRY OSK_DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); 27 int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int); 28 29 /* FUNCTIONS ******************************************************************/ 30 31 /*********************************************************************** 32 * 33 * OSK_SetImage 34 * 35 * Set an image on a button 36 */ 37 int OSK_SetImage(int IdDlgItem, int IdResource) 38 { 39 HICON hIcon; 40 HWND hWndItem; 41 42 hIcon = (HICON)LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IdResource), 43 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR); 44 if (hIcon == NULL) 45 return FALSE; 46 47 hWndItem = GetDlgItem(Globals.hMainWnd, IdDlgItem); 48 if (hWndItem == NULL) 49 { 50 DestroyIcon(hIcon); 51 return FALSE; 52 } 53 54 SendMessageW(hWndItem, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hIcon); 55 56 /* The system automatically deletes these resources when the process that created them terminates (MSDN) */ 57 58 return TRUE; 59 } 60 61 /*********************************************************************** 62 * 63 * OSK_WarningProc 64 * 65 * Function handler for the warning dialog box on startup 66 */ 67 INT_PTR CALLBACK OSK_WarningProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam) 68 { 69 UNREFERENCED_PARAMETER(lParam); 70 71 switch (Msg) 72 { 73 case WM_INITDIALOG: 74 { 75 return TRUE; 76 } 77 78 case WM_COMMAND: 79 { 80 switch (LOWORD(wParam)) 81 { 82 case IDC_SHOWWARNINGCHECK: 83 { 84 Globals.bShowWarning = !IsDlgButtonChecked(hDlg, IDC_SHOWWARNINGCHECK); 85 return TRUE; 86 } 87 88 case IDOK: 89 case IDCANCEL: 90 { 91 EndDialog(hDlg, LOWORD(wParam)); 92 return TRUE; 93 } 94 } 95 break; 96 } 97 } 98 99 return FALSE; 100 } 101 102 /*********************************************************************** 103 * 104 * OSK_About 105 * 106 * Initializes the "About" dialog box 107 */ 108 VOID OSK_About(VOID) 109 { 110 WCHAR szTitle[MAX_BUFF]; 111 WCHAR szAuthors[MAX_BUFF]; 112 HICON OSKIcon; 113 114 /* Load the icon */ 115 OSKIcon = LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IDI_OSK), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE); 116 117 /* Load the strings into the "About" dialog */ 118 LoadStringW(Globals.hInstance, STRING_OSK, szTitle, countof(szTitle)); 119 LoadStringW(Globals.hInstance, STRING_AUTHORS, szAuthors, countof(szAuthors)); 120 121 /* Finally, execute the "About" dialog by using the Shell routine */ 122 ShellAboutW(Globals.hMainWnd, szTitle, szAuthors, OSKIcon); 123 124 /* Once done, destroy the icon */ 125 DestroyIcon(OSKIcon); 126 } 127 128 129 /*********************************************************************** 130 * 131 * OSK_DlgInitDialog 132 * 133 * Handling of WM_INITDIALOG 134 */ 135 int OSK_DlgInitDialog(HWND hDlg) 136 { 137 HICON hIcon, hIconSm; 138 HMONITOR monitor; 139 MONITORINFO info; 140 POINT Pt; 141 RECT rcWindow; 142 143 /* Save handle */ 144 Globals.hMainWnd = hDlg; 145 146 /* Check the checked menu item before displaying the modal box */ 147 if (Globals.bIsEnhancedKeyboard) 148 { 149 /* Enhanced keyboard dialog chosen, set the respective menu item as checked */ 150 CheckMenuItem(GetMenu(hDlg), IDM_ENHANCED_KB, MF_BYCOMMAND | MF_CHECKED); 151 CheckMenuItem(GetMenu(hDlg), IDM_STANDARD_KB, MF_BYCOMMAND | MF_UNCHECKED); 152 } 153 else 154 { 155 /* Standard keyboard dialog chosen, set the respective menu item as checked */ 156 CheckMenuItem(GetMenu(hDlg), IDM_STANDARD_KB, MF_BYCOMMAND | MF_CHECKED); 157 CheckMenuItem(GetMenu(hDlg), IDM_ENHANCED_KB, MF_BYCOMMAND | MF_UNCHECKED); 158 } 159 160 /* Set the application's icon */ 161 hIcon = LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IDI_OSK), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE); 162 hIconSm = CopyImage(hIcon, IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_COPYFROMRESOURCE); 163 if (hIcon || hIconSm) 164 { 165 /* Set the window icons (they are deleted when the process terminates) */ 166 SendMessageW(Globals.hMainWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon); 167 SendMessageW(Globals.hMainWnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm); 168 } 169 170 /* Get screen info */ 171 memset(&Pt, 0, sizeof(Pt)); 172 monitor = MonitorFromPoint(Pt, MONITOR_DEFAULTTOPRIMARY); 173 info.cbSize = sizeof(info); 174 GetMonitorInfoW(monitor, &info); 175 176 /* Move the dialog on the bottom of main screen */ 177 GetWindowRect(hDlg, &rcWindow); 178 MoveWindow(hDlg, 179 (info.rcMonitor.left + info.rcMonitor.right) / 2 - // Center of screen 180 (rcWindow.right - rcWindow.left) / 2, // - half size of dialog 181 info.rcMonitor.bottom - // Bottom of screen 182 (rcWindow.bottom - rcWindow.top), // - size of window 183 rcWindow.right - rcWindow.left, // Width 184 rcWindow.bottom - rcWindow.top, // Height 185 TRUE); 186 187 /* Set icon on visual buttons */ 188 OSK_SetImage(SCAN_CODE_15, IDI_BACK); 189 OSK_SetImage(SCAN_CODE_16, IDI_TAB); 190 OSK_SetImage(SCAN_CODE_30, IDI_CAPS_LOCK); 191 OSK_SetImage(SCAN_CODE_43, IDI_RETURN); 192 OSK_SetImage(SCAN_CODE_44, IDI_SHIFT); 193 OSK_SetImage(SCAN_CODE_57, IDI_SHIFT); 194 OSK_SetImage(SCAN_CODE_127, IDI_REACTOS); 195 OSK_SetImage(SCAN_CODE_128, IDI_REACTOS); 196 OSK_SetImage(SCAN_CODE_129, IDI_MENU); 197 OSK_SetImage(SCAN_CODE_80, IDI_HOME); 198 OSK_SetImage(SCAN_CODE_85, IDI_PG_UP); 199 OSK_SetImage(SCAN_CODE_86, IDI_PG_DOWN); 200 OSK_SetImage(SCAN_CODE_79, IDI_LEFT); 201 OSK_SetImage(SCAN_CODE_83, IDI_TOP); 202 OSK_SetImage(SCAN_CODE_84, IDI_BOTTOM); 203 OSK_SetImage(SCAN_CODE_89, IDI_RIGHT); 204 205 /* Create a green brush for leds */ 206 Globals.hBrushGreenLed = CreateSolidBrush(RGB(0, 255, 0)); 207 208 /* Set a timer for periodics tasks */ 209 Globals.iTimer = SetTimer(hDlg, 0, 200, NULL); 210 211 return TRUE; 212 } 213 214 /*********************************************************************** 215 * 216 * OSK_DlgClose 217 * 218 * Handling of WM_CLOSE 219 */ 220 int OSK_DlgClose(void) 221 { 222 KillTimer(Globals.hMainWnd, Globals.iTimer); 223 224 /* Release Ctrl, Shift, Alt keys */ 225 OSK_ReleaseKey(SCAN_CODE_44); // Left shift 226 OSK_ReleaseKey(SCAN_CODE_57); // Right shift 227 OSK_ReleaseKey(SCAN_CODE_58); // Left ctrl 228 OSK_ReleaseKey(SCAN_CODE_60); // Left alt 229 OSK_ReleaseKey(SCAN_CODE_62); // Right alt 230 OSK_ReleaseKey(SCAN_CODE_64); // Right ctrl 231 232 /* delete GDI objects */ 233 if (Globals.hBrushGreenLed) DeleteObject(Globals.hBrushGreenLed); 234 235 /* Save the settings to the registry hive */ 236 SaveDataToRegistry(); 237 238 return TRUE; 239 } 240 241 /*********************************************************************** 242 * 243 * OSK_DlgTimer 244 * 245 * Handling of WM_TIMER 246 */ 247 int OSK_DlgTimer(void) 248 { 249 /* FIXME: To be deleted when ReactOS will support WS_EX_NOACTIVATE */ 250 HWND hWndActiveWindow; 251 252 hWndActiveWindow = GetForegroundWindow(); 253 if (hWndActiveWindow != NULL && hWndActiveWindow != Globals.hMainWnd) 254 { 255 Globals.hActiveWnd = hWndActiveWindow; 256 } 257 258 /* Always redraw leds because it can be changed by the real keyboard) */ 259 InvalidateRect(GetDlgItem(Globals.hMainWnd, IDC_LED_NUM), NULL, TRUE); 260 InvalidateRect(GetDlgItem(Globals.hMainWnd, IDC_LED_CAPS), NULL, TRUE); 261 InvalidateRect(GetDlgItem(Globals.hMainWnd, IDC_LED_SCROLL), NULL, TRUE); 262 263 return TRUE; 264 } 265 266 /*********************************************************************** 267 * 268 * OSK_DlgCommand 269 * 270 * All handling of dialog command 271 */ 272 BOOL OSK_DlgCommand(WPARAM wCommand, HWND hWndControl) 273 { 274 WORD ScanCode; 275 INPUT Input; 276 BOOL bExtendedKey; 277 BOOL bKeyDown; 278 BOOL bKeyUp; 279 LONG WindowStyle; 280 281 /* FIXME: To be deleted when ReactOS will support WS_EX_NOACTIVATE */ 282 if (Globals.hActiveWnd) 283 { 284 MSG msg; 285 286 SetForegroundWindow(Globals.hActiveWnd); 287 while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) 288 { 289 TranslateMessage(&msg); 290 DispatchMessageW(&msg); 291 } 292 } 293 294 /* KeyDown and/or KeyUp ? */ 295 WindowStyle = GetWindowLongW(hWndControl, GWL_STYLE); 296 if ((WindowStyle & BS_AUTOCHECKBOX) == BS_AUTOCHECKBOX) 297 { 298 /* 2-states key like Shift, Alt, Ctrl, ... */ 299 if (SendMessageW(hWndControl, BM_GETCHECK, 0, 0) == BST_CHECKED) 300 { 301 bKeyDown = TRUE; 302 bKeyUp = FALSE; 303 } 304 else 305 { 306 bKeyDown = FALSE; 307 bKeyUp = TRUE; 308 } 309 } 310 else 311 { 312 /* Other key */ 313 bKeyDown = TRUE; 314 bKeyUp = TRUE; 315 } 316 317 /* Extended key ? */ 318 ScanCode = wCommand; 319 if (ScanCode & 0x0200) 320 bExtendedKey = TRUE; 321 else 322 bExtendedKey = FALSE; 323 ScanCode &= 0xFF; 324 325 /* Press and release the key */ 326 if (bKeyDown) 327 { 328 Input.type = INPUT_KEYBOARD; 329 Input.ki.wVk = 0; 330 Input.ki.wScan = ScanCode; 331 Input.ki.time = GetTickCount(); 332 Input.ki.dwExtraInfo = GetMessageExtraInfo(); 333 Input.ki.dwFlags = KEYEVENTF_SCANCODE; 334 if (bExtendedKey) Input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; 335 SendInput(1, &Input, sizeof(Input)); 336 } 337 338 if (bKeyUp) 339 { 340 Input.type = INPUT_KEYBOARD; 341 Input.ki.wVk = 0; 342 Input.ki.wScan = ScanCode; 343 Input.ki.time = GetTickCount(); 344 Input.ki.dwExtraInfo = GetMessageExtraInfo(); 345 Input.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP; 346 if (bExtendedKey) Input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; 347 SendInput(1, &Input, sizeof(Input)); 348 } 349 350 return TRUE; 351 } 352 353 /*********************************************************************** 354 * 355 * OSK_ReleaseKey 356 * 357 * Release the key of ID wCommand 358 */ 359 BOOL OSK_ReleaseKey(WORD ScanCode) 360 { 361 INPUT Input; 362 BOOL bExtendedKey; 363 LONG WindowStyle; 364 HWND hWndControl; 365 366 /* Is it a 2-states key ? */ 367 hWndControl = GetDlgItem(Globals.hMainWnd, ScanCode); 368 WindowStyle = GetWindowLongW(hWndControl, GWL_STYLE); 369 if ((WindowStyle & BS_AUTOCHECKBOX) != BS_AUTOCHECKBOX) return FALSE; 370 371 /* Is the key down ? */ 372 if (SendMessageW(hWndControl, BM_GETCHECK, 0, 0) != BST_CHECKED) return TRUE; 373 374 /* Extended key ? */ 375 if (ScanCode & 0x0200) 376 bExtendedKey = TRUE; 377 else 378 bExtendedKey = FALSE; 379 ScanCode &= 0xFF; 380 381 /* Release the key */ 382 Input.type = INPUT_KEYBOARD; 383 Input.ki.wVk = 0; 384 Input.ki.wScan = ScanCode; 385 Input.ki.time = GetTickCount(); 386 Input.ki.dwExtraInfo = GetMessageExtraInfo(); 387 Input.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP; 388 if (bExtendedKey) Input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; 389 SendInput(1, &Input, sizeof(Input)); 390 391 return TRUE; 392 } 393 394 /*********************************************************************** 395 * 396 * OSK_DlgProc 397 */ 398 INT_PTR APIENTRY OSK_DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) 399 { 400 switch (msg) 401 { 402 case WM_INITDIALOG: 403 OSK_DlgInitDialog(hDlg); 404 return TRUE; 405 406 case WM_TIMER: 407 OSK_DlgTimer(); 408 return TRUE; 409 410 case WM_CTLCOLORSTATIC: 411 if ((HWND)lParam == GetDlgItem(hDlg, IDC_LED_NUM)) 412 { 413 if (GetKeyState(VK_NUMLOCK) & 0x0001) 414 return (INT_PTR)Globals.hBrushGreenLed; 415 else 416 return (INT_PTR)GetStockObject(BLACK_BRUSH); 417 } 418 if ((HWND)lParam == GetDlgItem(hDlg, IDC_LED_CAPS)) 419 { 420 if (GetKeyState(VK_CAPITAL) & 0x0001) 421 return (INT_PTR)Globals.hBrushGreenLed; 422 else 423 return (INT_PTR)GetStockObject(BLACK_BRUSH); 424 } 425 if ((HWND)lParam == GetDlgItem(hDlg, IDC_LED_SCROLL)) 426 { 427 if (GetKeyState(VK_SCROLL) & 0x0001) 428 return (INT_PTR)Globals.hBrushGreenLed; 429 else 430 return (INT_PTR)GetStockObject(BLACK_BRUSH); 431 } 432 break; 433 434 case WM_COMMAND: 435 switch (LOWORD(wParam)) 436 { 437 case IDCANCEL: 438 { 439 EndDialog(hDlg, FALSE); 440 break; 441 } 442 443 case IDM_EXIT: 444 { 445 EndDialog(hDlg, FALSE); 446 break; 447 } 448 449 case IDM_ENHANCED_KB: 450 { 451 if (!Globals.bIsEnhancedKeyboard) 452 { 453 /* 454 The user attempted to switch to enhanced keyboard dialog type. 455 Set the member value as TRUE, destroy the dialog and save the data configuration into the registry. 456 */ 457 Globals.bIsEnhancedKeyboard = TRUE; 458 EndDialog(hDlg, FALSE); 459 SaveDataToRegistry(); 460 461 /* Change the condition of enhanced keyboard item menu to checked */ 462 CheckMenuItem(GetMenu(hDlg), IDM_ENHANCED_KB, MF_BYCOMMAND | MF_CHECKED); 463 CheckMenuItem(GetMenu(hDlg), IDM_STANDARD_KB, MF_BYCOMMAND | MF_UNCHECKED); 464 465 /* Finally, display the dialog modal box with the enhanced keyboard dialog */ 466 DialogBoxW(Globals.hInstance, 467 MAKEINTRESOURCEW(MAIN_DIALOG_ENHANCED_KB), 468 GetDesktopWindow(), 469 OSK_DlgProc); 470 } 471 472 break; 473 } 474 475 case IDM_STANDARD_KB: 476 { 477 if (Globals.bIsEnhancedKeyboard) 478 { 479 /* 480 The user attempted to switch to standard keyboard dialog type. 481 Set the member value as FALSE, destroy the dialog and save the data configuration into the registry. 482 */ 483 Globals.bIsEnhancedKeyboard = FALSE; 484 EndDialog(hDlg, FALSE); 485 SaveDataToRegistry(); 486 487 /* Change the condition of standard keyboard item menu to checked */ 488 CheckMenuItem(GetMenu(hDlg), IDM_ENHANCED_KB, MF_BYCOMMAND | MF_UNCHECKED); 489 CheckMenuItem(GetMenu(hDlg), IDM_STANDARD_KB, MF_BYCOMMAND | MF_CHECKED); 490 491 /* Finally, display the dialog modal box with the standard keyboard dialog */ 492 DialogBoxW(Globals.hInstance, 493 MAKEINTRESOURCEW(MAIN_DIALOG_STANDARD_KB), 494 GetDesktopWindow(), 495 OSK_DlgProc); 496 } 497 498 break; 499 } 500 501 case IDM_ABOUT: 502 { 503 OSK_About(); 504 break; 505 } 506 507 default: 508 OSK_DlgCommand(wParam, (HWND)lParam); 509 break; 510 } 511 break; 512 513 case WM_CLOSE: 514 OSK_DlgClose(); 515 break; 516 } 517 518 return 0; 519 } 520 521 /*********************************************************************** 522 * 523 * WinMain 524 */ 525 int WINAPI wWinMain(HINSTANCE hInstance, 526 HINSTANCE prev, 527 LPWSTR cmdline, 528 int show) 529 { 530 HANDLE hMutex; 531 INT LayoutResource; 532 533 UNREFERENCED_PARAMETER(prev); 534 UNREFERENCED_PARAMETER(cmdline); 535 UNREFERENCED_PARAMETER(show); 536 537 ZeroMemory(&Globals, sizeof(Globals)); 538 Globals.hInstance = hInstance; 539 540 /* Load the settings from the registry hive */ 541 LoadDataFromRegistry(); 542 543 /* If the member of the struct (bShowWarning) is set then display the dialog box */ 544 if (Globals.bShowWarning) 545 { 546 DialogBoxW(Globals.hInstance, MAKEINTRESOURCEW(IDD_WARNINGDIALOG_OSK), Globals.hMainWnd, OSK_WarningProc); 547 } 548 549 /* Before initializing the dialog execution, check if the chosen keyboard type is standard or enhanced */ 550 if (Globals.bIsEnhancedKeyboard) 551 { 552 LayoutResource = MAIN_DIALOG_ENHANCED_KB; 553 } 554 else 555 { 556 LayoutResource = MAIN_DIALOG_STANDARD_KB; 557 } 558 559 /* Rry to open a mutex for a single instance */ 560 hMutex = OpenMutexW(MUTEX_ALL_ACCESS, FALSE, L"osk"); 561 562 if (!hMutex) 563 { 564 /* Mutex doesn�t exist. This is the first instance so create the mutex. */ 565 hMutex = CreateMutexW(NULL, FALSE, L"osk"); 566 567 /* Create the modal box based on the configuration registry */ 568 DialogBoxW(hInstance, 569 MAKEINTRESOURCEW(LayoutResource), 570 GetDesktopWindow(), 571 OSK_DlgProc); 572 573 /* Delete the mutex */ 574 if (hMutex) CloseHandle(hMutex); 575 } 576 else 577 { 578 /* Programme already launched */ 579 580 /* Delete the mutex */ 581 CloseHandle(hMutex); 582 583 ExitProcess(0); 584 } 585 586 return 0; 587 } 588 589 /* EOF */ 590