spiegel-keyman/developer/src/kmcmpdll/CasedKeys.cpp
Marc Durdin 5119f8d808 chore(windows): merge vkeys.h and move to common
Moves vkeys.h to common, turns it into a proper header and adds
corresponding vkeys.cpp so that we don't include the data multiple times
in a project.

Removes duplication of vkeys.h in kmcmpdll and global/inc.

Updates all projects to reference the common files, except kmdecomp,
coming in the next commit, as it is more complex.
2022-06-12 07:18:36 +10:00

149 lines
3.9 KiB
C++

#include "pch.h"
#include <compfile.h>
#include <comperr.h>
#include "../../../common/windows/cpp/include/vkeys.h"
#include <kmcmpdll.h>
#include "CharToKeyConversion.h"
extern BOOL FMnemonicLayout; // TODO: these globals should be consolidated one day
DWORD ExpandCapsRule(PFILE_GROUP gp, PFILE_KEY kpp, PFILE_STORE sp);
DWORD VerifyCasedKeys(PFILE_STORE sp) {
assert(sp != NULL);
if (FMnemonicLayout) {
// The &CasedKeys system store is not supported for
// mnemonic layouts in 14.0
return CERR_CasedKeysNotSupportedWithMnemonicLayout;
}
// We will rewrite this store with virtual keys
PWSTR p = sp->dpString;
PWSTR buf = new WCHAR[wcslen(p) * 5 + 1]; // extended keys are 5 units long, so this is the max length
PWSTR q = buf;
while (*p) {
UINT key = 0, shift = 0;
if (*p != UC_SENTINEL) {
if (!MapUSCharToVK(*p, &key, &shift)) {
return CERR_CasedKeysMustContainOnlyVirtualKeys;
}
if (shift & K_SHIFTFLAG) {
return CERR_CasedKeysMustNotIncludeShiftStates;
}
}
else {
if (*(p + 1) != CODE_EXTENDED) {
return CERR_CasedKeysMustContainOnlyVirtualKeys;
}
shift = *(p + 2);
key = *(p + 3);
if (shift != ISVIRTUALKEY) {
return CERR_CasedKeysMustNotIncludeShiftStates;
}
}
*q++ = UC_SENTINEL;
*q++ = CODE_EXTENDED;
*q++ = shift;
*q++ = key;
*q++ = UC_SENTINEL_EXTENDEDEND;
*q = 0;
p = incxstr(p);
}
delete[] sp->dpString;
sp->dpString = buf;
return CERR_None;
}
DWORD ExpandCapsRulesForGroup(PFILE_KEYBOARD fk, PFILE_GROUP gp) {
assert(fk != NULL);
assert(gp != NULL);
if (FMnemonicLayout) {
// The &CasedKeys system store is not supported for
// mnemonic layouts in 14.0
return CERR_None;
}
PFILE_STORE sp = FindSystemStore(fk, TSS_CASEDKEYS);
if (!sp) {
// If there is no &CasedKeys system store, then we do not
// process the key
return CERR_None;
}
DWORD msg;
// ExpandCapsRule may add extra rules at the end of gp->dpKeyArray,
// reallocating it, so we (a) cache the original length, and (b)
// dereference the array every call
int cxKeyArray = gp->cxKeyArray;
for (int i = 0; i < cxKeyArray; i++) {
if ((msg = ExpandCapsRule(gp, &gp->dpKeyArray[i], sp)) != CERR_None) {
return msg;
}
}
return CERR_None;
}
DWORD ExpandCapsRule(PFILE_GROUP gp, PFILE_KEY kpp, PFILE_STORE sp) {
UINT key = kpp->Key;
UINT shift = kpp->ShiftFlags;
if (shift == 0) {
// Convert US key cap to a virtual key
if (!MapUSCharToVK(kpp->Key, &key, &shift)) {
return CERR_None;
}
}
if (shift & (CAPITALFLAG | NOTCAPITALFLAG)) {
// Don't attempt expansion if either Caps Lock flag is specified in the key rule
return CERR_None;
}
PWSTR p = sp->dpString;
for (; *p; p = incxstr(p)) {
// We've already verified that the store contains only virtual keys in VerifyCasedKeys
if (*(p + 3) == key) {
break;
}
}
if (!*p) {
// This key is not modified by Caps Lock
return CERR_None;
}
// This key is modified by Caps Lock, so we need to duplicate this rule
PFILE_KEY k = new FILE_KEY[gp->cxKeyArray + 1];
if (!k) return CERR_CannotAllocateMemory;
memcpy(k, gp->dpKeyArray, gp->cxKeyArray * sizeof(FILE_KEY));
kpp = &k[(INT_PTR)(kpp - gp->dpKeyArray)];
delete gp->dpKeyArray;
gp->dpKeyArray = k;
gp->cxKeyArray++;
k = &k[gp->cxKeyArray - 1];
k->dpContext = new WCHAR[wcslen(kpp->dpContext) + 1];
k->dpOutput = new WCHAR[wcslen(kpp->dpOutput) + 1];
wcscpy_s(k->dpContext, wcslen(kpp->dpContext) + 1, kpp->dpContext); // copy the context.
wcscpy_s(k->dpOutput, wcslen(kpp->dpOutput) + 1, kpp->dpOutput); // copy the output.
k->Key = key;
k->Line = kpp->Line;
// Add the CAPITAL FLAG, invert shift flag for the rule
k->ShiftFlags = shift ^ K_SHIFTFLAG | CAPITALFLAG;
kpp->Key = key;
kpp->ShiftFlags = shift | NOTCAPITALFLAG;
return CERR_None;
}