xref: /reactos/base/applications/osk/main.c (revision 0493e8e4)
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