#include "stdafx.h"
#include "fixlayouts.h"
#include "utils.h"
#define HKL_HEBREW (HKL)0x040D040D
#define HKL_ENGLISH (HKL)0x04090409
///////////////////////////////////////////////////////////////////////////////
// Converts the text in the active window from one keyboard layout to another
// using the clipboard.
void ConvertSelectedTextInActiveWindow(HKL hklSource, HKL hklTarget)
{
WCHAR* sourceText = NULL;
WCHAR* targetText = NULL;
// store previous clipboard data and set clipboard to dummy string
ClipboardData prevClipboardData;
StoreClipboardData(&prevClipboardData);
// SetClipboardText(dummy);
DWORD dwSN = GetClipboardSequenceNumber();
// copy the selected text by simulating Ctrl-C
SendKeyCombo(VK_CONTROL, 'C', FALSE);
for (int i = 0;
dwSN == GetClipboardSequenceNumber() && i < 40; i++)
Sleep(30);
int nErr = dwSN != GetClipboardSequenceNumber() ? 0 : 2;
sourceText = GetClipboardText();
// if the string only matches one particular layout, use it
// otherwise use the provided layout
int matches = 0;
HKL hklDetected = DetectLayoutFromString(sourceText, &matches);
// PrintDebugString("Detected:%08x %d", hklDetected, matches);
if (matches == 1)
hklSource = hklDetected;
// convert the text between layouts
size_t length = wcslen(sourceText);
targetText = (WCHAR*)malloc(sizeof(WCHAR) * (length + 1));
size_t converted = LayoutConvertString(sourceText, targetText,
length + 1, hklSource, hklTarget);
// PrintDebugString(_T("Converted %d %08x -> %08x %s -> %s"),
// converted, hklSource, hklTarget, sourceText, targetText);
if (converted) {
dwSN = GetClipboardSequenceNumber();
// put the converted string on the clipboard
if (SetClipboardText(targetText)) {
// Sleep(100);
for (int i = 0;
dwSN == GetClipboardSequenceNumber() && i < 40; i++)
Sleep(30);
nErr += dwSN != GetClipboardSequenceNumber() ? 0 : 3;
// simulate Ctrl-V to paste the text, replacing the previous text
SendKeyCombo(VK_CONTROL, 'V', FALSE);
// let the application complete pasting before
// putting the old data back on the clipboard
Sleep(REMOTE_APP_WAIT);
}
}
// restore the original clipboard data
RestoreClipboardData(&prevClipboardData);
FreeClipboardData(&prevClipboardData);
// free allocated memory
free(sourceText);
free(targetText);
#if 1 // testing !
for (int i = 0; i < nErr; i++) {
MessageBeep(MB_ICONINFORMATION);
Sleep(300);
}
#endif
}
///////////////////////////////////////////////////////////////////////////////
// Converts a character from one keyboard layout to another
WCHAR LayoutConvertChar(WCHAR ch, HKL hklSource, HKL hklTarget)
{
// special handling for some ambivalent characters in hebrew layout
if (hklSource == HKL_HEBREW && hklTarget == HKL_ENGLISH) {
switch (ch) {
case L'.': return L'/';
case L'/': return L'q';
case L'\'': return L'w';
case L',': return L'\'';
}
} else if (hklSource == HKL_ENGLISH && hklTarget == HKL_HEBREW) {
switch (ch) {
case L'/': return L'.';
case L'q': return L'/';
case L'w': return L'\'';
case L'\'': return L',';
}
}
// get the virtual key code and the shift state using the
// character and the source keyboard layout
SHORT vkAndShift = VkKeyScanEx(ch, hklSource);
if (vkAndShift == -1)
return 0; // no such char in source keyboard layout
BYTE vk = LOBYTE(vkAndShift);
BYTE shift = HIBYTE(vkAndShift);
// convert the shift state returned from VkKeyScanEx
// to an array that represents the key state usable
// with ToUnicodeEx that we'll be calling next
BYTE keyState[256] = { 0 };
if (shift & 1) keyState[VK_SHIFT] = 0x80; // turn on high bit
if (shift & 2) keyState[VK_CONTROL] = 0x80;
if (shift & 4) keyState[VK_MENU] = 0x80;
// convert virtual key and key state to a new character
// using the target keyboard layout
WCHAR buffer[10] = { 0 };
int result = ToUnicodeEx(vk, 0, keyState, buffer, 10, 0, hklTarget);
// result can be more than 1 if the character in the source
// layout is represented by several characters in the target
// layout, but we ignore this to simplify the function.
if (result == 1 || result == -1)
return buffer[0];
// conversion failed for some reason
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// Converts a string from one keyboard layout to another
size_t LayoutConvertString(const WCHAR* str, WCHAR* buffer,
size_t size, HKL hklSource, HKL hklTarget)
{
size_t i;
for (i = 0; i < wcslen(str) && i < size - 1; i++) {
WCHAR ch = LayoutConvertChar(str[i], hklSource, hklTarget);
if (ch == 0)
return 0;
buffer[i] = ch;
}
buffer[i] = '\0';
return i;
}
///////////////////////////////////////////////////////////////////////////////
// Goes through all the installed keyboard layouts and returns a layout that
// can generate the string. If not matching layout is found, returns NULL.
// If `multiple` isn't NULL it will be set to the number of matched layouts.
HKL DetectLayoutFromString(const WCHAR* str, int* pmatches)
{
HKL result = NULL;
HKL* hkls;
UINT layoutCount;
layoutCount = GetKeyboardLayoutList(0, NULL);
hkls = (HKL*)malloc(sizeof(HKL) * layoutCount);
GetKeyboardLayoutList(layoutCount, hkls);
int matches = 0;
for (size_t layout = 0; layout < layoutCount; layout++) {
BOOL validLayout = TRUE;
for (size_t i = 0; i < wcslen(str); i++) {
UINT vk = VkKeyScanEx(str[i], hkls[layout]);
if (vk == -1) {
validLayout = FALSE;
break;
}
}
if (validLayout) {
matches++;
if (!result)
result = hkls[layout];
}
}
if (pmatches)
*pmatches = matches;
return result;
}
///////////////////////////////////////////////////////////////////////////////
// Stores the clipboard data in all its formats in `formats`.
// You must call FreeAllClipboardData on `formats` when it's no longer needed.
BOOL StoreClipboardData(ClipboardData* formats)
{
if (OpenClipboard(NULL)) {
formats->count = CountClipboardFormats();
formats->dataArray = (ClipboardFormat*)malloc(
sizeof(ClipboardData) * formats->count);
ZeroMemory(formats->dataArray, sizeof(ClipboardData) * formats->count);
int i = 0;
UINT format = EnumClipboardFormats(0);
while (format) {
HANDLE dataHandle = GetClipboardData(format);
LPVOID source = NULL;
size_t size = 0;
if (NULL != dataHandle
&& NULL != (source = GlobalLock(dataHandle))
&& 0 != (size = GlobalSize(dataHandle))) {
formats->dataArray[i].format = format;
formats->dataArray[i].dataHandle = GlobalAlloc(GHND, size);
LPVOID dest = GlobalLock(formats->dataArray[i].dataHandle);
CopyMemory(dest, source, size);
GlobalUnlock(formats->dataArray[i].dataHandle);
GlobalUnlock(dataHandle);
}
// next format
format = EnumClipboardFormats(format);
i++;
}
CloseClipboard();
return TRUE;
}
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
// Restores the data in the clipboard from `formats` that was generated by
// StoreClipboardData.
BOOL RestoreClipboardData(const ClipboardData* formats)
{
if (OpenClipboard(NULL)) {
EmptyClipboard();
for (int i = 0; i < formats->count; i++)
SetClipboardData(
formats->dataArray[i].format, formats->dataArray[i].dataHandle);
CloseClipboard();
return TRUE;
}
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
// Frees `formats` allocated by StoreClipboardData
void FreeClipboardData(ClipboardData* formats)
{
free(formats->dataArray);
}
///////////////////////////////////////////////////////////////////////////////
// Gets unicode text from the clipboard.
// You must free the returned string when you don't need it anymore.
WCHAR* GetClipboardText()
{
WCHAR* text = NULL;
if (OpenClipboard(NULL)) {
HANDLE handle = GetClipboardData(CF_UNICODETEXT);
WCHAR* clipboardText = (WCHAR*)GlobalLock(handle);
if (!clipboardText)
return NULL;
size_t size = sizeof(WCHAR) * (wcslen(clipboardText)+1);
text = (WCHAR*)malloc(size);
if (!text)
return NULL;
memcpy(text, clipboardText, size);
CloseClipboard();
}
return text;
}
///////////////////////////////////////////////////////////////////////////////
// Puts unicode text on the clipboard
BOOL SetClipboardText(const WCHAR* text)
{
if (OpenClipboard(NULL)) {
size_t size = sizeof(WCHAR) * (wcslen(text)+1);
HANDLE handle = GlobalAlloc(GHND, size);
WCHAR* clipboardText = (WCHAR*)GlobalLock(handle);
memcpy(clipboardText, text, size);
GlobalUnlock(handle);
EmptyClipboard();
SetClipboardData(CF_UNICODETEXT, handle);
CloseClipboard();
return TRUE;
}
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
// Simulates a key press in the active window
void SendKey(BYTE vk, BOOL extended)
{
keybd_event(vk, 0, extended ? KEYEVENTF_EXTENDEDKEY : 0, 0);
keybd_event(vk, 0,
KEYEVENTF_KEYUP | (extended ? KEYEVENTF_EXTENDEDKEY : 0), 0);
}
///////////////////////////////////////////////////////////////////////////////
// Simulates a key combination (such as Ctrl+X) in the active window
void SendKeyCombo(BYTE vkModifier, BYTE vk, BOOL extended)
{
BOOL modPressed = (GetKeyState(vkModifier) & 0x80000000) > 0;
#if 0
if (!modPressed)
keybd_event(vkModifier, 0, 0, 0);
keybd_event(vk, 0, extended ? KEYEVENTF_EXTENDEDKEY : 0, 0);
if (!modPressed)
keybd_event(vkModifier, 0, KEYEVENTF_KEYUP, 0);
keybd_event(vk, 0,
KEYEVENTF_KEYUP | (extended ? KEYEVENTF_EXTENDEDKEY : 0), 0);
#else
int count = 0;
INPUT input[] = {
{ INPUT_KEYBOARD, 0 },
{ INPUT_KEYBOARD, 0 },
{ INPUT_KEYBOARD, 0 },
{ INPUT_KEYBOARD, 0 },
};
if (!modPressed)
input[count++].ki.wVk = vkModifier;
input[count].ki.wVk = vk;
input[count++].ki.dwFlags = extended ? KEYEVENTF_EXTENDEDKEY : 0;
if (!modPressed) {
input[count].ki.wVk = vkModifier;
input[count++].ki.dwFlags = KEYEVENTF_KEYUP;
}
input[count].ki.wVk = vk;
input[count++].ki.dwFlags = KEYEVENTF_KEYUP | (extended ? KEYEVENTF_EXTENDEDKEY : 0);
UINT u = ::SendInput(count, input, sizeof(INPUT));
/*#ifdef _DEBUG
_stprintf(ch, _T("SendInput:%d %d\n"), u, GetLastError());
OutputDebugString(ch);
#endif*/
#endif
}