xref: /reactos/base/applications/osk/main.c (revision 71fefa32)
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 _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, 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)LoadImage(Globals.hInstance, MAKEINTRESOURCE(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     SendMessage(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  *
105  *           OSK_DlgInitDialog
106  *
107  *  Handling of WM_INITDIALOG
108  */
109 int OSK_DlgInitDialog(HWND hDlg)
110 {
111     HICON hIcon, hIconSm;
112     HMONITOR monitor;
113     MONITORINFO info;
114     POINT Pt;
115     RECT rcWindow;
116 
117     /* Save handle */
118     Globals.hMainWnd = hDlg;
119 
120     /* Load the settings from the registry hive */
121     LoadDataFromRegistry();
122 
123     /* Set the application's icon */
124     hIcon = LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IDI_OSK), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE);
125     hIconSm = CopyImage(hIcon, IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_COPYFROMRESOURCE);
126     if (hIcon || hIconSm)
127     {
128         /* Set the window icons (they are deleted when the process terminates) */
129         SendMessageW(Globals.hMainWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
130         SendMessageW(Globals.hMainWnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm);
131     }
132 
133     /* Get screen info */
134     memset(&Pt, 0, sizeof(Pt));
135     monitor = MonitorFromPoint(Pt, MONITOR_DEFAULTTOPRIMARY);
136     info.cbSize = sizeof(info);
137     GetMonitorInfoW(monitor, &info);
138 
139     /* Move the dialog on the bottom of main screen */
140     GetWindowRect(hDlg, &rcWindow);
141     MoveWindow(hDlg,
142                (info.rcMonitor.left + info.rcMonitor.right) / 2 - // Center of screen
143                    (rcWindow.right - rcWindow.left) / 2,          // - half size of dialog
144                info.rcMonitor.bottom -               // Bottom of screen
145                    (rcWindow.bottom - rcWindow.top), // - size of window
146                rcWindow.right - rcWindow.left,     // Width
147                rcWindow.bottom - rcWindow.top,     // Height
148                TRUE);
149 
150     /* Set icon on visual buttons */
151     OSK_SetImage(SCAN_CODE_15, IDI_BACK);
152     OSK_SetImage(SCAN_CODE_16, IDI_TAB);
153     OSK_SetImage(SCAN_CODE_30, IDI_CAPS_LOCK);
154     OSK_SetImage(SCAN_CODE_43, IDI_RETURN);
155     OSK_SetImage(SCAN_CODE_44, IDI_SHIFT);
156     OSK_SetImage(SCAN_CODE_57, IDI_SHIFT);
157     OSK_SetImage(SCAN_CODE_127, IDI_REACTOS);
158     OSK_SetImage(SCAN_CODE_128, IDI_REACTOS);
159     OSK_SetImage(SCAN_CODE_129, IDI_MENU);
160     OSK_SetImage(SCAN_CODE_80, IDI_HOME);
161     OSK_SetImage(SCAN_CODE_85, IDI_PG_UP);
162     OSK_SetImage(SCAN_CODE_86, IDI_PG_DOWN);
163     OSK_SetImage(SCAN_CODE_79, IDI_LEFT);
164     OSK_SetImage(SCAN_CODE_83, IDI_TOP);
165     OSK_SetImage(SCAN_CODE_84, IDI_BOTTOM);
166     OSK_SetImage(SCAN_CODE_89, IDI_RIGHT);
167 
168     /* Create a green brush for leds */
169     Globals.hBrushGreenLed = CreateSolidBrush(RGB(0, 255, 0));
170 
171     /* Set a timer for periodics tasks */
172     Globals.iTimer = SetTimer(hDlg, 0, 200, NULL);
173 
174     /* If the member of the struct (bShowWarning) is set then display the dialog box */
175     if (Globals.bShowWarning)
176     {
177         DialogBox(Globals.hInstance, MAKEINTRESOURCE(IDD_WARNINGDIALOG_OSK), Globals.hMainWnd, OSK_WarningProc);
178     }
179 
180     return TRUE;
181 }
182 
183 /***********************************************************************
184  *
185  *           OSK_DlgClose
186  *
187  *  Handling of WM_CLOSE
188  */
189 int OSK_DlgClose(void)
190 {
191     KillTimer(Globals.hMainWnd, Globals.iTimer);
192 
193     /* Release Ctrl, Shift, Alt keys */
194     OSK_ReleaseKey(SCAN_CODE_44); // Left shift
195     OSK_ReleaseKey(SCAN_CODE_57); // Right shift
196     OSK_ReleaseKey(SCAN_CODE_58); // Left ctrl
197     OSK_ReleaseKey(SCAN_CODE_60); // Left alt
198     OSK_ReleaseKey(SCAN_CODE_62); // Right alt
199     OSK_ReleaseKey(SCAN_CODE_64); // Right ctrl
200 
201     /* delete GDI objects */
202     if (Globals.hBrushGreenLed) DeleteObject(Globals.hBrushGreenLed);
203 
204     /* Save the settings to the registry hive */
205     SaveDataToRegistry();
206 
207     return TRUE;
208 }
209 
210 /***********************************************************************
211  *
212  *           OSK_DlgTimer
213  *
214  *  Handling of WM_TIMER
215  */
216 int OSK_DlgTimer(void)
217 {
218     /* FIXME: To be deleted when ReactOS will support WS_EX_NOACTIVATE */
219     HWND hWndActiveWindow;
220 
221     hWndActiveWindow = GetForegroundWindow();
222     if (hWndActiveWindow != NULL && hWndActiveWindow != Globals.hMainWnd)
223     {
224         Globals.hActiveWnd = hWndActiveWindow;
225     }
226 
227     /* Always redraw leds because it can be changed by the real keyboard) */
228     InvalidateRect(GetDlgItem(Globals.hMainWnd, IDC_LED_NUM), NULL, TRUE);
229     InvalidateRect(GetDlgItem(Globals.hMainWnd, IDC_LED_CAPS), NULL, TRUE);
230     InvalidateRect(GetDlgItem(Globals.hMainWnd, IDC_LED_SCROLL), NULL, TRUE);
231 
232     return TRUE;
233 }
234 
235 /***********************************************************************
236  *
237  *           OSK_DlgCommand
238  *
239  *  All handling of dialog command
240  */
241 BOOL OSK_DlgCommand(WPARAM wCommand, HWND hWndControl)
242 {
243     WORD ScanCode;
244     INPUT Input;
245     BOOL bExtendedKey;
246     BOOL bKeyDown;
247     BOOL bKeyUp;
248     LONG WindowStyle;
249 
250     /* FIXME: To be deleted when ReactOS will support WS_EX_NOACTIVATE */
251     if (Globals.hActiveWnd)
252     {
253         MSG msg;
254 
255         SetForegroundWindow(Globals.hActiveWnd);
256         while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
257         {
258             TranslateMessage(&msg);
259             DispatchMessage(&msg);
260         }
261     }
262 
263     /* KeyDown and/or KeyUp ? */
264     WindowStyle = GetWindowLong(hWndControl, GWL_STYLE);
265     if ((WindowStyle & BS_AUTOCHECKBOX) == BS_AUTOCHECKBOX)
266     {
267         /* 2-states key like Shift, Alt, Ctrl, ... */
268         if (SendMessage(hWndControl, BM_GETCHECK, 0, 0) == BST_CHECKED)
269         {
270             bKeyDown = TRUE;
271             bKeyUp = FALSE;
272         }
273         else
274         {
275             bKeyDown = FALSE;
276             bKeyUp = TRUE;
277         }
278     }
279     else
280     {
281         /* Other key */
282         bKeyDown = TRUE;
283         bKeyUp = TRUE;
284     }
285 
286     /* Extended key ? */
287     ScanCode = wCommand;
288     if (ScanCode & 0x0200)
289         bExtendedKey = TRUE;
290     else
291         bExtendedKey = FALSE;
292     ScanCode &= 0xFF;
293 
294     /* Press and release the key */
295     if (bKeyDown)
296     {
297         Input.type = INPUT_KEYBOARD;
298         Input.ki.wVk = 0;
299         Input.ki.wScan = ScanCode;
300         Input.ki.time = GetTickCount();
301         Input.ki.dwExtraInfo = GetMessageExtraInfo();
302         Input.ki.dwFlags = KEYEVENTF_SCANCODE;
303         if (bExtendedKey) Input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
304         SendInput(1, &Input, sizeof(Input));
305     }
306 
307     if (bKeyUp)
308     {
309         Input.type = INPUT_KEYBOARD;
310         Input.ki.wVk = 0;
311         Input.ki.wScan = ScanCode;
312         Input.ki.time = GetTickCount();
313         Input.ki.dwExtraInfo = GetMessageExtraInfo();
314         Input.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP;
315         if (bExtendedKey) Input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
316         SendInput(1, &Input, sizeof(Input));
317     }
318 
319     return TRUE;
320 }
321 
322 /***********************************************************************
323  *
324  *           OSK_ReleaseKey
325  *
326  *  Release the key of ID wCommand
327  */
328 BOOL OSK_ReleaseKey(WORD ScanCode)
329 {
330     INPUT Input;
331     BOOL bExtendedKey;
332     LONG WindowStyle;
333     HWND hWndControl;
334 
335     /* Is it a 2-states key ? */
336     hWndControl = GetDlgItem(Globals.hMainWnd, ScanCode);
337     WindowStyle = GetWindowLong(hWndControl, GWL_STYLE);
338     if ((WindowStyle & BS_AUTOCHECKBOX) != BS_AUTOCHECKBOX) return FALSE;
339 
340     /* Is the key down ? */
341     if (SendMessage(hWndControl, BM_GETCHECK, 0, 0) != BST_CHECKED) return TRUE;
342 
343     /* Extended key ? */
344     if (ScanCode & 0x0200)
345         bExtendedKey = TRUE;
346     else
347         bExtendedKey = FALSE;
348     ScanCode &= 0xFF;
349 
350     /* Release the key */
351     Input.type = INPUT_KEYBOARD;
352     Input.ki.wVk = 0;
353     Input.ki.wScan = ScanCode;
354     Input.ki.time = GetTickCount();
355     Input.ki.dwExtraInfo = GetMessageExtraInfo();
356     Input.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP;
357     if (bExtendedKey) Input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
358     SendInput(1, &Input, sizeof(Input));
359 
360     return TRUE;
361 }
362 
363 /***********************************************************************
364  *
365  *       OSK_DlgProc
366  */
367 INT_PTR APIENTRY OSK_DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
368 {
369     switch (msg)
370     {
371         case WM_INITDIALOG:
372             OSK_DlgInitDialog(hDlg);
373             return TRUE;
374 
375         case WM_TIMER:
376             OSK_DlgTimer();
377             return TRUE;
378 
379         case WM_CTLCOLORSTATIC:
380             if ((HWND)lParam == GetDlgItem(hDlg, IDC_LED_NUM))
381             {
382                 if (GetKeyState(VK_NUMLOCK) & 0x0001)
383                     return (INT_PTR)Globals.hBrushGreenLed;
384                 else
385                     return (INT_PTR)GetStockObject(BLACK_BRUSH);
386             }
387             if ((HWND)lParam == GetDlgItem(hDlg, IDC_LED_CAPS))
388             {
389                 if (GetKeyState(VK_CAPITAL) & 0x0001)
390                     return (INT_PTR)Globals.hBrushGreenLed;
391                 else
392                     return (INT_PTR)GetStockObject(BLACK_BRUSH);
393             }
394             if ((HWND)lParam == GetDlgItem(hDlg, IDC_LED_SCROLL))
395             {
396                 if (GetKeyState(VK_SCROLL) & 0x0001)
397                     return (INT_PTR)Globals.hBrushGreenLed;
398                 else
399                     return (INT_PTR)GetStockObject(BLACK_BRUSH);
400             }
401             break;
402 
403         case WM_COMMAND:
404             if (wParam == IDCANCEL)
405                 EndDialog(hDlg, FALSE);
406             else if (wParam != IDC_STATIC)
407                 OSK_DlgCommand(wParam, (HWND) lParam);
408             break;
409 
410         case WM_CLOSE:
411             OSK_DlgClose();
412             break;
413     }
414 
415     return 0;
416 }
417 
418 /***********************************************************************
419  *
420  *       WinMain
421  */
422 int WINAPI _tWinMain(HINSTANCE hInstance,
423                      HINSTANCE prev,
424                      LPTSTR cmdline,
425                      int show)
426 {
427     HANDLE hMutex;
428 
429     UNREFERENCED_PARAMETER(prev);
430     UNREFERENCED_PARAMETER(cmdline);
431     UNREFERENCED_PARAMETER(show);
432 
433     ZeroMemory(&Globals, sizeof(Globals));
434     Globals.hInstance = hInstance;
435 
436     /* Rry to open a mutex for a single instance */
437     hMutex = OpenMutexA(MUTEX_ALL_ACCESS, FALSE, "osk");
438 
439     if (!hMutex)
440     {
441         /* Mutex doesn�t exist. This is the first instance so create the mutex. */
442         hMutex = CreateMutexA(NULL, FALSE, "osk");
443 
444         DialogBox(hInstance,
445                   MAKEINTRESOURCE(MAIN_DIALOG),
446                   GetDesktopWindow(),
447                   OSK_DlgProc);
448 
449         /* Delete the mutex */
450         if (hMutex) CloseHandle(hMutex);
451     }
452     else
453     {
454         /* Programme already launched */
455 
456         /* Delete the mutex */
457         CloseHandle(hMutex);
458 
459         ExitProcess(0);
460     }
461 
462     return 0;
463 }
464 
465 /* EOF */
466