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