Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
victor-homyakov committed Oct 22, 2014
1 parent fec9044 commit ee556fe
Show file tree
Hide file tree
Showing 20 changed files with 1,722 additions and 3 deletions.
38 changes: 35 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,36 @@
recaps
======
Recaps 0.6
------------------------------------

Using CapsLock to switch the keyboard language
This simple program allows you to switch between languages using the mostly-unused CapsLock key instead of the clumsy Alt+Shift combination.

Just press CapsLock and the language will change. You can use Alt-CapsLock if you still need the old CapsLock action - to write a lot of text in capital letters.

If you accidentally typed some text in the wrong layout, Ctrl-Capslock should fix it.

If you have more than two languages installed, you can select the ones you want to cycle through if you right click the application's icon.

Enjoy!

Eli Golovinsky
http://www.gooli.org/


Revision history
------------------------------------
0.6 Added conversion of text typed with the wrong layout
using Ctrl-CapsLock. Alt-CapsLock now changes the old
CapsLock mode.

0.5 Fixed selected languages configuration not being saved
in the registry.

0.4 Fixed language not being switched in some applications.

0.3 Added a tray icon, a menu and support for more than two
languages. Recaps now cycles between the lanugages selected
in the menu.

0.2 Changed Shift-CapsLock to change language as well as CapsLock.
Ctrl-CapsLock now changes the old CapsLock mode.

0.1 Initial release
37 changes: 37 additions & 0 deletions build.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@echo off

set FILENAME=recaps-0.6

:: clean up
if exist Output\*.* del /q Output\*.*
if exist Release\nul rd /s/q Release
if exist src\nul rd /s/q src

:: build binaries
devenv recaps.2005.sln /rebuild Release
if errorlevel 1 goto error

:: build binary distribution using Inno Setup
iscc /F%FILENAME%-setup setup.iss
if errorlevel 1 goto error

:: copy standalone exe
copy Release\recaps.exe Output\%FILENAME%.exe
if errorlevel 1 goto error

:: build source distribution using 7zip
mkdir src
copy *.* src
del src\*.ncb
del src\*.aps
del src\*.user
7z -tzip a Output\%FILENAME%-src.zip src/*
if errorlevel 1 goto error
rd /s/q src

goto end

:error
pause

:end
311 changes: 311 additions & 0 deletions fixlayouts.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
#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;
const WCHAR dummy [] = L"__RECAPS__";

// store previous clipboard data and set clipboard to dummy string
ClipboardData prevClipboardData;
StoreClipboardData(&prevClipboardData);

SetClipboardText(dummy);

// copy the selected text by simulating Ctrl-C
SendKeyCombo(VK_CONTROL, 'C', FALSE);

// wait until copy operation completes and get the copied data from the clipboard
// this loop has the nice side effect of setting copyOK to FALSE if there's no
// selected text, since nothing is copied and the clipboard still contains the
// contents of `dummy`.
BOOL copyOK = FALSE;
for(int i = 0; i < 10; i++)
{
sourceText = GetClipboardText();
if (sourceText && wcscmp(sourceText, dummy) != 0)
{
copyOK = TRUE;
break;
}
else
{
free(sourceText);
Sleep(30);
}
}

if (copyOK)
{
// if the string only matches one particular layout, use it
// otherwise use the provided layout
int matches = 0;
HKL hklDetected = DetectLayoutFromString(sourceText, &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);

if (converted)
{
// put the converted string on the clipboard
if (SetClipboardText(targetText))
{
// 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);
}
}

///////////////////////////////////////////////////////////////////////////////
// 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)
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 = GlobalLock(dataHandle);
size_t 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 (!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);
}

Loading

0 comments on commit ee556fe

Please sign in to comment.