/*
* PROJECT: ReactOS On-Screen Keyboard
* LICENSE: GPL - See COPYING in the top level directory
- * FILE: base/applications/osk/main.c
* PURPOSE: On-screen keyboard.
- * PROGRAMMERS: Denis ROBERT
+ * COPYRIGHT: Denis ROBERT
+ * Copyright 2019 Bișoc George (fraizeraust99 at gmail dot com)
*/
/* INCLUDES *******************************************************************/
BOOL OSK_ReleaseKey(WORD ScanCode);
INT_PTR APIENTRY OSK_DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
-int WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int);
+LRESULT APIENTRY OSK_ThemeHandler(HWND hDlg, NMCUSTOMDRAW *pNmDraw);
+int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int);
/* FUNCTIONS ******************************************************************/
HICON hIcon;
HWND hWndItem;
- hIcon = (HICON)LoadImage(Globals.hInstance, MAKEINTRESOURCE(IdResource),
- IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
+ hIcon = (HICON)LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IdResource),
+ IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
if (hIcon == NULL)
return FALSE;
return FALSE;
}
- SendMessage(hWndItem, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hIcon);
+ SendMessageW(hWndItem, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hIcon);
/* The system automatically deletes these resources when the process that created them terminates (MSDN) */
return TRUE;
}
-/***********************************************************************
- *
- * OSK_SetAppIcon
- *
- * Set the application's icon
- */
-BOOL OSK_SetAppIcon()
-{
- HICON hIconSmall;
-
- /* Load the icon */
- hIconSmall = LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IDI_OSK), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
-
- /* Send a message window indicating that the icon has to be set */
- SendMessageW(Globals.hMainWnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSmall);
-
- if (!hIconSmall)
- {
- /* If we fail then return FALSE and bail out */
- DestroyIcon(hIconSmall);
- return FALSE;
- }
-
- return TRUE;
-}
-
/***********************************************************************
*
* OSK_WarningProc
return FALSE;
}
+/***********************************************************************
+ *
+ * OSK_About
+ *
+ * Initializes the "About" dialog box
+ */
+VOID OSK_About(VOID)
+{
+ WCHAR szTitle[MAX_BUFF];
+ WCHAR szAuthors[MAX_BUFF];
+ HICON OSKIcon;
+
+ /* Load the icon */
+ OSKIcon = LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IDI_OSK), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
+
+ /* Load the strings into the "About" dialog */
+ LoadStringW(Globals.hInstance, STRING_OSK, szTitle, countof(szTitle));
+ LoadStringW(Globals.hInstance, STRING_AUTHORS, szAuthors, countof(szAuthors));
+
+ /* Finally, execute the "About" dialog by using the Shell routine */
+ ShellAboutW(Globals.hMainWnd, szTitle, szAuthors, OSKIcon);
+
+ /* Once done, destroy the icon */
+ DestroyIcon(OSKIcon);
+}
+
/***********************************************************************
*
*/
int OSK_DlgInitDialog(HWND hDlg)
{
- HMONITOR monitor;
+ HICON hIcon, hIconSm;
+ HMONITOR monitor;
MONITORINFO info;
POINT Pt;
- RECT rcWindow;
+ RECT rcWindow, rcDlgIntersect;
/* Save handle */
Globals.hMainWnd = hDlg;
- /* Load the settings from the registry hive */
- LoadDataFromRegistry();
+ /* Check the checked menu item before displaying the modal box */
+ if (Globals.bIsEnhancedKeyboard)
+ {
+ /* Enhanced keyboard dialog chosen, set the respective menu item as checked */
+ CheckMenuItem(GetMenu(hDlg), IDM_ENHANCED_KB, MF_BYCOMMAND | MF_CHECKED);
+ CheckMenuItem(GetMenu(hDlg), IDM_STANDARD_KB, MF_BYCOMMAND | MF_UNCHECKED);
+ }
+ else
+ {
+ /* Standard keyboard dialog chosen, set the respective menu item as checked */
+ CheckMenuItem(GetMenu(hDlg), IDM_STANDARD_KB, MF_BYCOMMAND | MF_CHECKED);
+ CheckMenuItem(GetMenu(hDlg), IDM_ENHANCED_KB, MF_BYCOMMAND | MF_UNCHECKED);
+ }
+
+ /* Check if the "Click Sound" option was chosen before (and if so, then tick the menu item) */
+ if (Globals.bSoundClick)
+ {
+ CheckMenuItem(GetMenu(hDlg), IDM_CLICK_SOUND, MF_BYCOMMAND | MF_CHECKED);
+ }
/* Set the application's icon */
- OSK_SetAppIcon();
+ hIcon = LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IDI_OSK), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE);
+ hIconSm = CopyImage(hIcon, IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_COPYFROMRESOURCE);
+ if (hIcon || hIconSm)
+ {
+ /* Set the window icons (they are deleted when the process terminates) */
+ SendMessageW(Globals.hMainWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
+ SendMessageW(Globals.hMainWnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm);
+ }
/* Get screen info */
memset(&Pt, 0, sizeof(Pt));
- monitor = MonitorFromPoint(Pt, MONITOR_DEFAULTTOPRIMARY );
+ monitor = MonitorFromPoint(Pt, MONITOR_DEFAULTTOPRIMARY);
info.cbSize = sizeof(info);
GetMonitorInfoW(monitor, &info);
-
- /* Move the dialog on the bottom of main screen */
GetWindowRect(hDlg, &rcWindow);
- MoveWindow(hDlg,
- (info.rcMonitor.left + info.rcMonitor.right) / 2 - // Center of screen
- (rcWindow.right - rcWindow.left) / 2, // - half size of dialog
- info.rcMonitor.bottom - // Bottom of screen
- (rcWindow.bottom - rcWindow.top), // - size of window
- rcWindow.right - rcWindow.left, // Width
- rcWindow.bottom - rcWindow.top, // Height
- TRUE);
+
+ /*
+ If the coordination values are default then re-initialize using the specific formulas
+ to move the dialog at the bottom of the screen.
+ */
+ if (Globals.PosX == CW_USEDEFAULT && Globals.PosY == CW_USEDEFAULT)
+ {
+ Globals.PosX = (info.rcMonitor.left + info.rcMonitor.right - (rcWindow.right - rcWindow.left)) / 2;
+ Globals.PosY = info.rcMonitor.bottom - (rcWindow.bottom - rcWindow.top);
+ }
+
+ /*
+ Calculate the intersection of two rectangle sources (dialog and work desktop area).
+ If such sources do not intersect, then the dialog is deemed as "off screen".
+ */
+ if (IntersectRect(&rcDlgIntersect, &rcWindow, &info.rcWork) == 0)
+ {
+ Globals.PosX = (info.rcMonitor.left + info.rcMonitor.right - (rcWindow.right - rcWindow.left)) / 2;
+ Globals.PosY = info.rcMonitor.bottom - (rcWindow.bottom - rcWindow.top);
+ }
+ else
+ {
+ /*
+ There's still some intersection but we're not for sure if it is sufficient (the dialog could also be partially hidden).
+ Therefore, check the remaining intersection if it's enough.
+ */
+ if (rcWindow.top < info.rcWork.top || rcWindow.left < info.rcWork.left || rcWindow.right > info.rcWork.right || rcWindow.bottom > info.rcWork.bottom)
+ {
+ Globals.PosX = (info.rcMonitor.left + info.rcMonitor.right - (rcWindow.right - rcWindow.left)) / 2;
+ Globals.PosY = info.rcMonitor.bottom - (rcWindow.bottom - rcWindow.top);
+ }
+ }
+
+ /* Move the dialog according to the placement coordination */
+ SetWindowPos(hDlg, HWND_TOP, Globals.PosX, Globals.PosY, 0, 0, SWP_NOSIZE);
/* Set icon on visual buttons */
OSK_SetImage(SCAN_CODE_15, IDI_BACK);
/* Set a timer for periodics tasks */
Globals.iTimer = SetTimer(hDlg, 0, 200, NULL);
- /* If the member of the struct (bShowWarning) is set then display the dialog box */
- if (Globals.bShowWarning)
- {
- DialogBox(Globals.hInstance, MAKEINTRESOURCE(IDD_WARNINGDIALOG_OSK), Globals.hMainWnd, OSK_WarningProc);
- }
-
return TRUE;
}
MSG msg;
SetForegroundWindow(Globals.hActiveWnd);
- while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
+ while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
- DispatchMessage(&msg);
+ DispatchMessageW(&msg);
}
}
/* KeyDown and/or KeyUp ? */
- WindowStyle = GetWindowLong(hWndControl, GWL_STYLE);
+ WindowStyle = GetWindowLongW(hWndControl, GWL_STYLE);
if ((WindowStyle & BS_AUTOCHECKBOX) == BS_AUTOCHECKBOX)
{
/* 2-states key like Shift, Alt, Ctrl, ... */
- if (SendMessage(hWndControl, BM_GETCHECK, 0, 0) == BST_CHECKED)
+ if (SendMessageW(hWndControl, BM_GETCHECK, 0, 0) == BST_CHECKED)
{
bKeyDown = TRUE;
bKeyUp = FALSE;
SendInput(1, &Input, sizeof(Input));
}
+ /* Play the sound during clicking event (only if "Use Click Sound" menu option is ticked) */
+ if (Globals.bSoundClick)
+ {
+ PlaySoundW(MAKEINTRESOURCEW(IDI_SOUNDCLICK), GetModuleHandle(NULL), SND_RESOURCE | SND_ASYNC);
+ }
+
return TRUE;
}
/* Is it a 2-states key ? */
hWndControl = GetDlgItem(Globals.hMainWnd, ScanCode);
- WindowStyle = GetWindowLong(hWndControl, GWL_STYLE);
+ WindowStyle = GetWindowLongW(hWndControl, GWL_STYLE);
if ((WindowStyle & BS_AUTOCHECKBOX) != BS_AUTOCHECKBOX) return FALSE;
/* Is the key down ? */
- if (SendMessage(hWndControl, BM_GETCHECK, 0, 0) != BST_CHECKED) return TRUE;
+ if (SendMessageW(hWndControl, BM_GETCHECK, 0, 0) != BST_CHECKED) return TRUE;
/* Extended key ? */
if (ScanCode & 0x0200)
return TRUE;
}
+/***********************************************************************
+ *
+ * OSK_ThemeHandler
+ *
+ * Function helper which handles theme drawing of controls
+ */
+LRESULT APIENTRY OSK_ThemeHandler(HWND hDlg, NMCUSTOMDRAW *pNmDraw)
+{
+ HTHEME hTheme;
+ HWND hDlgButtonCtrl;
+ INT iState = PBS_NORMAL;
+
+ /* Retrieve the theme handle for the button controls */
+ hDlgButtonCtrl = pNmDraw->hdr.hwndFrom;
+ hTheme = GetWindowTheme(hDlgButtonCtrl);
+
+ /*
+ Begin the painting procedures if we retrieved
+ the theme for control buttons of the dialog.
+ */
+ if (hTheme)
+ {
+ /*
+ The button could be either in normal state or pushed.
+ Retrieve its state and save to a variable.
+ */
+ if (pNmDraw->uItemState & CDIS_DEFAULT)
+ {
+ iState = PBS_DEFAULTED;
+ }
+ else if (pNmDraw->uItemState & CDIS_SELECTED)
+ {
+ iState = PBS_PRESSED;
+ }
+ else if (pNmDraw->uItemState & CDIS_HOT)
+ {
+ iState = PBS_HOT;
+ }
+
+ if (IsThemeBackgroundPartiallyTransparent(hTheme, BP_PUSHBUTTON, iState))
+ {
+ /* Draw the application if the theme is transparent */
+ DrawThemeParentBackground(hDlg, pNmDraw->hdc, &pNmDraw->rc);
+ }
+
+ /* Draw it */
+ DrawThemeBackground(hTheme, pNmDraw->hdc, BP_PUSHBUTTON, iState, &pNmDraw->rc, NULL);
+ }
+
+ return CDRF_SKIPDEFAULT;
+}
+
/***********************************************************************
*
* OSK_DlgProc
OSK_DlgTimer();
return TRUE;
+ case WM_NOTIFY:
+ return OSK_ThemeHandler(hDlg, (LPNMCUSTOMDRAW)lParam);
+
case WM_CTLCOLORSTATIC:
if ((HWND)lParam == GetDlgItem(hDlg, IDC_LED_NUM))
{
break;
case WM_COMMAND:
- if (wParam == IDCANCEL)
- EndDialog(hDlg, FALSE);
- else if (wParam != IDC_STATIC)
- OSK_DlgCommand(wParam, (HWND) lParam);
+ switch (LOWORD(wParam))
+ {
+ case IDCANCEL:
+ {
+ EndDialog(hDlg, FALSE);
+ break;
+ }
+
+ case IDM_EXIT:
+ {
+ EndDialog(hDlg, FALSE);
+ break;
+ }
+
+ case IDM_ENHANCED_KB:
+ {
+ if (!Globals.bIsEnhancedKeyboard)
+ {
+ /*
+ The user attempted to switch to enhanced keyboard dialog type.
+ Set the member value as TRUE, destroy the dialog and save the data configuration into the registry.
+ */
+ Globals.bIsEnhancedKeyboard = TRUE;
+ EndDialog(hDlg, FALSE);
+ SaveDataToRegistry();
+
+ /* Change the condition of enhanced keyboard item menu to checked */
+ CheckMenuItem(GetMenu(hDlg), IDM_ENHANCED_KB, MF_BYCOMMAND | MF_CHECKED);
+ CheckMenuItem(GetMenu(hDlg), IDM_STANDARD_KB, MF_BYCOMMAND | MF_UNCHECKED);
+
+ /* Finally, display the dialog modal box with the enhanced keyboard dialog */
+ DialogBoxW(Globals.hInstance,
+ MAKEINTRESOURCEW(MAIN_DIALOG_ENHANCED_KB),
+ GetDesktopWindow(),
+ OSK_DlgProc);
+ }
+
+ break;
+ }
+
+ case IDM_STANDARD_KB:
+ {
+ if (Globals.bIsEnhancedKeyboard)
+ {
+ /*
+ The user attempted to switch to standard keyboard dialog type.
+ Set the member value as FALSE, destroy the dialog and save the data configuration into the registry.
+ */
+ Globals.bIsEnhancedKeyboard = FALSE;
+ EndDialog(hDlg, FALSE);
+ SaveDataToRegistry();
+
+ /* Change the condition of standard keyboard item menu to checked */
+ CheckMenuItem(GetMenu(hDlg), IDM_ENHANCED_KB, MF_BYCOMMAND | MF_UNCHECKED);
+ CheckMenuItem(GetMenu(hDlg), IDM_STANDARD_KB, MF_BYCOMMAND | MF_CHECKED);
+
+ /* Finally, display the dialog modal box with the standard keyboard dialog */
+ DialogBoxW(Globals.hInstance,
+ MAKEINTRESOURCEW(MAIN_DIALOG_STANDARD_KB),
+ GetDesktopWindow(),
+ OSK_DlgProc);
+ }
+
+ break;
+ }
+
+ case IDM_CLICK_SOUND:
+ {
+ /*
+ This case is triggered when the user attempts to click on the menu item. Before doing anything,
+ we must check the condition state of such menu item so that we can tick/untick the menu item accordingly.
+ */
+ if (!Globals.bSoundClick)
+ {
+ Globals.bSoundClick = TRUE;
+ CheckMenuItem(GetMenu(hDlg), IDM_CLICK_SOUND, MF_BYCOMMAND | MF_CHECKED);
+ }
+ else
+ {
+ Globals.bSoundClick = FALSE;
+ CheckMenuItem(GetMenu(hDlg), IDM_CLICK_SOUND, MF_BYCOMMAND | MF_UNCHECKED);
+ }
+
+ break;
+ }
+
+ case IDM_ABOUT:
+ {
+ OSK_About();
+ break;
+ }
+
+ default:
+ OSK_DlgCommand(wParam, (HWND)lParam);
+ break;
+ }
+ break;
+
+ case WM_THEMECHANGED:
+ /* Redraw the dialog (and its control buttons) using the new theme */
+ InvalidateRect(hDlg, NULL, FALSE);
break;
case WM_CLOSE:
*
* WinMain
*/
-int WINAPI _tWinMain(HINSTANCE hInstance,
- HINSTANCE prev,
- LPTSTR cmdline,
- int show)
+int WINAPI wWinMain(HINSTANCE hInstance,
+ HINSTANCE prev,
+ LPWSTR cmdline,
+ int show)
{
HANDLE hMutex;
+ DWORD dwError;
+ INT LayoutResource;
+ INITCOMMONCONTROLSEX iccex;
UNREFERENCED_PARAMETER(prev);
UNREFERENCED_PARAMETER(cmdline);
UNREFERENCED_PARAMETER(show);
+ /*
+ Obtain a mutex for the program. This will ensure that
+ the program is launched only once.
+ */
+ hMutex = CreateMutexW(NULL, FALSE, L"OSKRunning");
+
+ if (hMutex)
+ {
+ /* Check if there's already a mutex for the program */
+ dwError = GetLastError();
+
+ if (dwError == ERROR_ALREADY_EXISTS)
+ {
+ /*
+ A mutex with the object name has been created previously.
+ Therefore, another instance is already running.
+ */
+ DPRINT("wWinMain(): Failed to create a mutex! The program instance is already running.\n");
+ CloseHandle(hMutex);
+ return 0;
+ }
+ }
+
+ /* Load the common controls */
+ iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ iccex.dwICC = ICC_STANDARD_CLASSES | ICC_WIN95_CLASSES;
+ InitCommonControlsEx(&iccex);
+
ZeroMemory(&Globals, sizeof(Globals));
Globals.hInstance = hInstance;
- /* Rry to open a mutex for a single instance */
- hMutex = OpenMutexA(MUTEX_ALL_ACCESS, FALSE, "osk");
+ /* Load the settings from the registry hive */
+ LoadDataFromRegistry();
- if (!hMutex)
+ /* If the member of the struct (bShowWarning) is set then display the dialog box */
+ if (Globals.bShowWarning)
{
- /* Mutex doesn\92t exist. This is the first instance so create the mutex. */
- hMutex = CreateMutexA(NULL, FALSE, "osk");
-
- DialogBox(hInstance,
- MAKEINTRESOURCE(MAIN_DIALOG),
- GetDesktopWindow(),
- OSK_DlgProc);
+ DialogBoxW(Globals.hInstance, MAKEINTRESOURCEW(IDD_WARNINGDIALOG_OSK), Globals.hMainWnd, OSK_WarningProc);
+ }
- /* Delete the mutex */
- if (hMutex) CloseHandle(hMutex);
+ /* Before initializing the dialog execution, check if the chosen keyboard type is standard or enhanced */
+ if (Globals.bIsEnhancedKeyboard)
+ {
+ LayoutResource = MAIN_DIALOG_ENHANCED_KB;
}
else
{
- /* Programme already launched */
+ LayoutResource = MAIN_DIALOG_STANDARD_KB;
+ }
- /* Delete the mutex */
- CloseHandle(hMutex);
+ /* Create the modal box based on the configuration registry */
+ DialogBoxW(hInstance,
+ MAKEINTRESOURCEW(LayoutResource),
+ GetDesktopWindow(),
+ OSK_DlgProc);
- ExitProcess(0);
+ /* Delete the mutex */
+ if (hMutex)
+ {
+ CloseHandle(hMutex);
}
return 0;