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