chore(web): Merge branch 'master' into chore/web/merge-master-esmodule-a17s13-mid

This commit is contained in:
Joshua A. Horton 2023-05-22 13:49:45 +07:00
commit ca894d0991
108 changed files with 1621 additions and 3234 deletions

View file

@ -1,5 +1,36 @@
# Keyman Version History
## 17.0.109 alpha 2023-05-17
* fix(windows): add wrap-symbols to Text Editor Makefile (#8819)
* chore(linux): Make postinst script comply with Debian policy (#8810)
* chore(windows): remove legacy core and flag (#8593)
## 17.0.108 alpha 2023-05-16
* feat(windows): add text editor to the support makefile (#8750)
* feat(developer): verify keyboard versions in kmc-package (#8769)
* feat(developer): verify bcp47 tags are valid and minimal in kmc-package (#8778)
* feat(developer): verify at least one language in package (#8783)
* chore(developer): verify file types of content files in package (#8792)
* chore(developer): verify that package has at least a model or keyboard (#8793)
* chore(ios): Changes required for XCode 14.3 (#8746)
## 17.0.107 alpha 2023-05-15
* chore(linux): Fix installation build step on TC (#8784)
* refactor(android/engine): Consolidate updateSelection (#8739)
## 17.0.106 alpha 2023-05-12
* chore(developer): move package formats to common/web/types (#8729)
* feat(developer): add package validation (#8740)
* feat(developer): add validation of package filenames (#8751)
* feat(developer): validate content file names in packages (#8755)
* feat(developer): validate package name in compiler (#8757)
* chore(developer): rename Compiler and related classes (#8726)
* feat(developer): uset api from wasm! (#8716)
## 17.0.105 alpha 2023-05-11
* chore(common): Update crowdin strings for Amharic (#8748)

View file

@ -1 +1 @@
17.0.106
17.0.110

View file

@ -47,6 +47,9 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.webkit.ConsoleMessage;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
@ -139,7 +142,7 @@ final class KMKeyboard extends WebView {
}
public boolean getShouldShowHelpBubble() {
if(this._shouldShowHelpBubble == null) {
if (this._shouldShowHelpBubble == null) {
SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
this._shouldShowHelpBubble = prefs.getBoolean(KMManager.KMKey_ShouldShowHelpBubble, true);
}
@ -151,10 +154,54 @@ final class KMKeyboard extends WebView {
this._shouldShowHelpBubble = flag;
}
protected boolean shouldIgnoreTextChange() { return shouldIgnoreTextChange; }
protected void setShouldIgnoreTextChange(boolean ignore) { this.shouldIgnoreTextChange = ignore; }
protected boolean shouldIgnoreSelectionChange() { return shouldIgnoreSelectionChange; }
protected void setShouldIgnoreSelectionChange(boolean ignore) { this.shouldIgnoreSelectionChange = ignore; }
protected boolean shouldIgnoreTextChange() {
return shouldIgnoreTextChange;
}
protected void setShouldIgnoreTextChange(boolean ignore) {
this.shouldIgnoreTextChange = ignore;
}
protected boolean shouldIgnoreSelectionChange() {
return shouldIgnoreSelectionChange;
}
protected void setShouldIgnoreSelectionChange(boolean ignore) {
this.shouldIgnoreSelectionChange = ignore;
}
protected boolean updateText(String text) {
boolean result = false;
String kmText = "";
if (text != null) {
kmText = text.toString().replace("\\", "\\u005C").replace("'", "\\u0027").replace("\n", "\\n");
}
if (KMManager.isKeyboardLoaded(this.keyboardType) && !shouldIgnoreTextChange) {
this.loadJavascript(KMString.format("updateKMText('%s')", kmText));
result = true;
}
shouldIgnoreTextChange = false;
return result;
}
protected boolean updateSelectionRange(int selStart, int selEnd) {
boolean result = false;
InputConnection ic = KMManager.getInputConnection(this.keyboardType);
if (ic != null) {
ExtractedText icText = ic.getExtractedText(new ExtractedTextRequest(), 0);
if (icText != null) {
updateText(icText.text.toString());
}
}
this.loadJavascript(KMString.format("updateKMSelectionRange(%d,%d)", selStart, selEnd));
result = true;
return result;
}
@SuppressWarnings("deprecation")
@SuppressLint("SetJavaScriptEnabled")

View file

@ -1952,25 +1952,11 @@ public final class KMManager {
public static boolean updateText(KeyboardType kbType, String text) {
boolean result = false;
String kmText = "";
if (text != null) {
kmText = text.toString().replace("\\", "\\u005C").replace("'", "\\u0027").replace("\n", "\\n");
}
if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP) {
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP) && !InAppKeyboard.shouldIgnoreTextChange()) {
InAppKeyboard.loadJavascript(KMString.format("updateKMText('%s')", kmText));
result = true;
}
InAppKeyboard.setShouldIgnoreTextChange(false);
return InAppKeyboard.updateText(text);
} else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM) && !SystemKeyboard.shouldIgnoreTextChange()) {
SystemKeyboard.loadJavascript(KMString.format("updateKMText('%s')", kmText));
result = true;
}
SystemKeyboard.setShouldIgnoreTextChange(false);
return SystemKeyboard.updateText(text);
}
return result;
@ -1980,23 +1966,13 @@ public final class KMManager {
boolean result = false;
if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP) {
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP) && !InAppKeyboard.shouldIgnoreSelectionChange()) {
InAppKeyboard.loadJavascript(KMString.format("updateKMSelectionRange(%d,%d)", selStart, selEnd));
result = true;
result = InAppKeyboard.updateSelectionRange(selStart, selEnd);
}
InAppKeyboard.setShouldIgnoreSelectionChange(false);
} else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM) && !SystemKeyboard.shouldIgnoreSelectionChange()) {
InputConnection ic = getInputConnection(KeyboardType.KEYBOARD_TYPE_SYSTEM);
if (ic != null) {
ExtractedText icText = ic.getExtractedText(new ExtractedTextRequest(), 0);
if (icText != null) {
updateText(kbType, icText.text.toString());
}
}
SystemKeyboard.loadJavascript(KMString.format("updateKMSelectionRange(%d,%d)", selStart, selEnd));
result = true;
result = SystemKeyboard.updateSelectionRange(selStart, selEnd);
}
SystemKeyboard.setShouldIgnoreSelectionChange(false);

View file

@ -18,7 +18,7 @@ export { default as LDMLKeyboardXMLSourceFileReader } from './ldml-keyboard/ldml
export * as Constants from './consts/virtual-key-constants.js';
export { CompilerCallbacks, CompilerSchema, CompilerEvent, CompilerErrorNamespace, CompilerErrorSeverity, CompilerPathCallbacks, CompilerFileSystemCallbacks, CompilerMessageSpec, compilerErrorSeverityName } from './util/compiler-interfaces.js';
export { CompilerCallbacks, CompilerSchema, CompilerEvent, CompilerErrorNamespace, CompilerErrorSeverity, CompilerPathCallbacks, CompilerFileSystemCallbacks, CompilerMessageSpec, compilerErrorSeverityName, compilerExceptionToString, compilerErrorFormatCode } from './util/compiler-interfaces.js';
export { CommonTypesMessages } from './util/common-events.js';
export * as TouchLayout from './keyman-touch-layout/keyman-touch-layout-file.js';

View file

@ -32,6 +32,18 @@ export function compilerErrorSeverityName(code: number): string {
}
}
/**
* Format the error code number
* example: "FATAL:0x03004"
*/
export function compilerErrorFormatCode(code: number): string {
const severity = code & CompilerErrorSeverity.Severity_Mask;
const severityName = compilerErrorSeverityName(severity);
const errorCode = code & CompilerErrorSeverity.Error_Mask;
const errorCodeString = Number(errorCode).toString(16).padStart(5,'0');
return `${severityName}:0x${errorCodeString}`;
}
/**
* Defines the error code ranges for various compilers. Once defined, these
* ranges must not be changed as external modules may depend on specific error
@ -137,3 +149,10 @@ export interface CompilerCallbacks {
* @returns
*/
export const CompilerMessageSpec = (code: number, message: string) : CompilerEvent => { return { code, message } };
/**
* @param e Error-like
*/
export function compilerExceptionToString(e?: any) : string {
return `${(e ?? 'unknown error').toString()}\n\nCall stack:\n${(e instanceof Error ? e.stack : (new Error()).stack)}`;
}

View file

@ -129,9 +129,6 @@
#define REGSZ_Flag_UseCachedHotkeyModifierState "Flag_UseCachedHotkeyModifierState"
/* REGSZ_Flag_UseKeymanCore DWORD: Turns on the common core - instead of windows core */
#define REGSZ_Flag_UseKeymanCore "Flag_UseKeymanCore"
/* DWORD: Enable/disable deep TSF integration, default enabled; 0 = disabled, 1 = enabled, 2 = default */
#define REGSZ_DeepTSFIntegration "deep tsf integration"

View file

@ -1,6 +1,6 @@
import * as fs from 'fs';
import * as path from 'path';
import { CompilerEvent, CompilerCallbacks, CompilerSchema, CompilerPathCallbacks, CompilerFileSystemCallbacks } from '@keymanapp/common-types';
import { CompilerEvent, CompilerCallbacks, CompilerSchema, CompilerPathCallbacks, CompilerFileSystemCallbacks, compilerErrorSeverityName } from '@keymanapp/common-types';
export { verifyCompilerMessagesObject } from './verifyCompilerMessagesObject.js';
// TODO: schemas are only used by kmc-ldml for now, so this works at this
@ -19,6 +19,17 @@ export class TestCompilerCallbacks implements CompilerCallbacks {
this.messages = [];
}
printMessages() {
this.messages.forEach(event => {
const code = event.code.toString(16);
if(event.line) {
console.log(`${compilerErrorSeverityName(event.code)} ${code} [${event.line}]: ${event.message}`);
} else {
console.log(`${compilerErrorSeverityName(event.code)} ${code}: ${event.message}`);
}
});
}
hasMessage(code: number): boolean {
return this.messages.find((item) => item.code == code) === undefined ? false : true;
}

View file

@ -19,13 +19,13 @@ import {assert, expect} from 'chai';
const toTitleCase = (s: string) => s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
export function verifyCompilerMessagesObject(source: Record<string,any>) {
let keys = Object.keys(source);
const keys = Object.keys(source);
const m = source as Record<string,any>;
let codes: number[] = [];
const codes: number[] = [];
for(let key of keys) {
for(const key of keys) {
// Verify each object member matches the pattern we expect
@ -36,7 +36,7 @@ export function verifyCompilerMessagesObject(source: Record<string,any>) {
const c = o[1].toUpperCase() + '_' + o[2];
expect(m[c]).to.be.a('number', `Expected constant name ${c} to exist`);
let v = m[key]('','','','','','','','','','','','' /* ignore arguments*/);
const v = m[key]('','','','','','','','','','','','' /* ignore arguments*/);
expect(v.code).to.equal(m[c], `Function ${key} returns the wrong code`);
}
else if(typeof m[key] == 'number') {
@ -63,4 +63,4 @@ export function verifyCompilerMessagesObject(source: Record<string,any>) {
codes.push(code);
}
}
}
}

View file

@ -22,7 +22,7 @@ TODO: implement additional interfaces:
*/
// TODO: rename wasm-host?
import { CompilerCallbacks } from '@keymanapp/common-types';
import { CompilerCallbacks, CompilerEvent } from '@keymanapp/common-types';
import loadWasmHost from '../import/kmcmplib/wasm-host.js';
import { CompilerMessages, mapErrorFromKmcmplib } from './messages.js';
@ -47,33 +47,87 @@ const baseOptions: CompilerOptions = {
*/
let callbackProcIdentifier = 0;
export class Compiler {
wasmModule: any;
compileKeyboardFile: any;
setCompilerOptions: any;
/**
* Pointer in wasm-space
*/
type WasmPtr = number;
/**
* The wrapped functions
*/
class WasmWrapper {
Module: any;
compileKeyboardFile?: (pszInfile: string, pszOutfile: string, aSaveDebug: number, aCompilerWarningsAsErrors: number, aWarnDeprecatedCode: number, msgProc: string) => boolean;
parseUnicodeSet?: (pat: string, buf: WasmPtr, length: number) => number;
setCompilerOptions?: (shouldAddCompilerVersion: number) => boolean;
constructor(wasmModule: any) {
this.Module = wasmModule;
if (!wasmModule) {
throw Error(`wasm host did not load`);
}
this.compileKeyboardFile = this.Module.cwrap('kmcmp_Wasm_CompileKeyboardFile', 'boolean', ['string', 'string', 'number', 'number', 'number', 'string']);
this.parseUnicodeSet = this.Module.cwrap('kmcmp_Wasm_ParseUnicodeSet', 'number', ['string', 'number', 'number']);
this.setCompilerOptions = this.Module.cwrap('kmcmp_Wasm_SetCompilerOptions', 'boolean', ['number']);
if (this.parseUnicodeSet === undefined
|| this.setCompilerOptions === undefined
|| this.compileKeyboardFile === undefined) {
throw Error(`some wasm functions did not load properly.`);
}
}
/**
* Entry point into Wasm functions
* @returns WasmWrapper
*/
public static async load() : Promise<WasmWrapper> {
return new WasmWrapper(await loadWasmHost());
}
};
export class KmnCompiler {
callbackName: string;
callbacks: CompilerCallbacks;
wasm: WasmWrapper;
constructor() {
this.callbackName = 'kmnCompilerCallback' + callbackProcIdentifier;
callbackProcIdentifier++;
}
public async init(): Promise<boolean> {
if(!this.wasmModule) {
this.wasmModule = await loadWasmHost();
this.compileKeyboardFile = this.wasmModule.cwrap('kmcmp_Wasm_CompileKeyboardFile', 'boolean', ['string', 'string',
'number', 'number', 'number', 'string']);
this.setCompilerOptions = this.wasmModule.cwrap('kmcmp_Wasm_SetCompilerOptions', 'boolean', ['number']);
public async init(callbacks: CompilerCallbacks): Promise<boolean> {
this.callbacks = callbacks;
if(!this.wasm) {
try {
this.wasm = await WasmWrapper.load();
} catch(e: any) {
this.callbacks.reportMessage(CompilerMessages.Fatal_MissingWasmModule({e}));
return false;
}
}
return this.compileKeyboardFile !== undefined && this.setCompilerOptions !== undefined;
return this.verifyInitialized();
}
public run(infile: string, outfile: string, callbacks: CompilerCallbacks, options?: CompilerOptions): boolean {
this.callbacks = callbacks;
/**
* Verify that wasm is spun up OK.
* @returns true if OK
*/
public verifyInitialized() : boolean {
if(!this.callbacks) {
// Can't report a message here.
throw Error('Must call Compiler.init(callbacks) before proceeding');
}
if(!this.wasm) { // fail if wasm not loaded or function not found
this.callbacks.reportMessage(CompilerMessages.Fatal_MissingWasmModule({}));
return false;
}
return true;
}
if(!this.wasmModule) {
this.callbacks.reportMessage(CompilerMessages.Fatal_MissingWasmModule());
public run(infile: string, outfile: string, options?: CompilerOptions): boolean {
if(!this.verifyInitialized()) {
return false;
}
@ -92,10 +146,10 @@ export class Compiler {
private runCompiler(infile: string, outfile: string, options: CompilerOptions): boolean {
try {
if(!this.setCompilerOptions(options.shouldAddCompilerVersion)) {
if (!this.wasm.setCompilerOptions(options.shouldAddCompilerVersion ? 1 : 0)) {
this.callbacks.reportMessage(CompilerMessages.Fatal_UnableToSetCompilerOptions());
}
return this.compileKeyboardFile(
return this.wasm.compileKeyboardFile(
infile,
outfile,
options.saveDebug ? 1 : 0,
@ -107,4 +161,85 @@ export class Compiler {
return false;
}
}
}
/**
*
* @param pattern UnicodeSet pattern such as `[a-z]`
* @param bufferSize guess as to the buffer size
* @returns UnicodeSet accessor object, or null on failure
*/
public parseUnicodeSet(pattern: string, bufferSize: number) : UnicodeSet | null {
if(!this.verifyInitialized()) {
return null;
}
if (!bufferSize) {
bufferSize = 100; // TODO-LDML: Preflight mode? Reuse buffer?
}
const { Module } = this.wasm;
const buf = Module.asm.malloc(bufferSize * 2 * Module.HEAPU32.BYTES_PER_ELEMENT);
// TODO-LDML: Catch OOM
const rc = this.wasm.parseUnicodeSet(pattern, buf, bufferSize);
if (rc >= 0) {
const ranges = [];
const startu = (buf / Module.HEAPU32.BYTES_PER_ELEMENT);
for (let i = 0; i < rc; i++) {
const low = Module.HEAPU32[startu + (i * 2) + 0];
const high = Module.HEAPU32[startu + (i * 2) + 1];
ranges.push([low, high]);
}
// TODO-LDML: no free??
// Module.asm.free(buf);
return new UnicodeSet(pattern, ranges);
} else {
// translate error
// TODO-LDML: no free??
// Module.asm.free(buf);
this.callbacks.reportMessage(getUnicodeSetError(rc));
return null;
}
}
}
/**
* translate UnicodeSet return code into a compiler event
* @param rc parseUnicodeSet error code
* @returns the compiler event
*/
function getUnicodeSetError(rc: number) : CompilerEvent {
// from kmcmplib.h
const KMCMP_ERROR_SYNTAX_ERR = -1;
const KMCMP_ERROR_HAS_STRINGS = -2;
const KMCMP_ERROR_UNSUPPORTED_PROPERTY = -3;
const KMCMP_FATAL_OUT_OF_RANGE = -4;
switch(rc) {
case KMCMP_ERROR_SYNTAX_ERR:
return CompilerMessages.Error_UnicodeSetSyntaxError();
case KMCMP_ERROR_HAS_STRINGS:
return CompilerMessages.Error_UnicodeSetHasStrings();
case KMCMP_ERROR_UNSUPPORTED_PROPERTY:
return CompilerMessages.Error_UnicodeSetHasProperties();
case KMCMP_FATAL_OUT_OF_RANGE:
return CompilerMessages.Fatal_UnicodeSetOutOfRange();
default:
return CompilerMessages.Fatal_UnexpectedException({e: `Unexpected UnicodeSet error code ${rc}`});
}
}
/**
* Represents a parsed UnicodeSet
*/
export class UnicodeSet {
constructor(public pattern: string, public ranges: number[][]) {
}
/**
* Number of ranges
*/
get length() : number {
return this.ranges.length;
}
toString() : string {
return this.pattern;
}
}

View file

@ -1,4 +1,4 @@
import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerEvent, CompilerMessageSpec as m } from "@keymanapp/common-types";
import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerEvent, CompilerMessageSpec as m, compilerExceptionToString as exc } from "@keymanapp/common-types";
const Namespace = CompilerErrorNamespace.KmnCompiler;
const SevInfo = CompilerErrorSeverity.Info | Namespace;
@ -44,14 +44,29 @@ export const enum KmnCompilerMessageRanges {
and the below ranges are reserved.
*/
export class CompilerMessages {
static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, `Unexpected exception: ${(o.e ?? 'unknown error').toString()}\n\nCall stack:\n${(o.e instanceof Error ? o.e.stack : (new Error()).stack)}`);
static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, `Unexpected exception: ${exc(o.e)}`);
static FATAL_UnexpectedException = SevFatal | 0x1000;
static Fatal_MissingWasmModule = () => m(this.FATAL_MissingWasmModule, `Could not instanatiate WASM compiler module`);
static Fatal_MissingWasmModule = (o:{e?: any}) => m(this.FATAL_MissingWasmModule, `Could not instantiate WASM compiler module or initialization failed: ${exc(o.e)}`);
static FATAL_MissingWasmModule = SevFatal | 0x1001;
static Fatal_UnableToSetCompilerOptions = () => m(this.FATAL_UnableToSetCompilerOptions, `Unable to set compiler options`);
static FATAL_UnableToSetCompilerOptions = SevFatal | 0x1002;
static Fatal_CallbacksNotSet = () => m(this.FATAL_CallbacksNotSet, `Callbacks were not set with init`);
static FATAL_CallbacksNotSet = SevFatal | 0x1003;
static Fatal_UnicodeSetOutOfRange = () => m(this.FATAL_UnicodeSetOutOfRange, `UnicodeSet buffer was too small`);
static FATAL_UnicodeSetOutOfRange = SevFatal | 0x1004;
static Error_UnicodeSetHasStrings = () => m(this.ERROR_UnicodeSetHasStrings, `UnicodeSet contains strings, not allowed`);
static ERROR_UnicodeSetHasStrings = SevError | 0x1005;
static Error_UnicodeSetHasProperties = () => m(this.ERROR_UnicodeSetHasProperties, `UnicodeSet contains properties, not allowed`);
static ERROR_UnicodeSetHasProperties = SevError | 0x1006;
static Error_UnicodeSetSyntaxError = () => m(this.ERROR_UnicodeSetSyntaxError, `UnicodeSet had a Syntax Error while parsing`);
static ERROR_UnicodeSetSyntaxError = SevError | 0x1007;
}
export function mapErrorFromKmcmplib(line: number, code: number, msg: string): CompilerEvent {

View file

@ -1,2 +1,2 @@
export { Compiler } from './compiler/compiler.js';
export { KmnCompiler } from './compiler/compiler.js';

View file

@ -2,7 +2,7 @@ import 'mocha';
import sinon from 'sinon';
import chai, { assert } from 'chai';
import sinonChai from 'sinon-chai';
import { Compiler } from '../src/main.js';
import { KmnCompiler } from '../src/main.js';
import { dirname } from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
@ -15,6 +15,7 @@ chai.use(sinonChai);
describe('Compiler class', function() {
let consoleLog: any;
// TODO: do we need this?
beforeEach(function() {
consoleLog = sinon.spy(console, 'log');
});
@ -23,21 +24,36 @@ describe('Compiler class', function() {
consoleLog.restore();
});
it('should throw on failure', async function() {
const compiler = new KmnCompiler();
const callbacks : any = null; // ERROR
try {
await compiler.init(callbacks)
assert.fail('Expected exception');
} catch(e) {
assert.ok(e);
}
assert.throws(() => compiler.verifyInitialized());
});
it('should start', async function() {
const compiler = new Compiler();
assert(await compiler.init());
const compiler = new KmnCompiler();
const callbacks = new TestCompilerCallbacks();
assert(await compiler.init(callbacks));
assert(compiler.verifyInitialized());
});
it('should compile a basic keyboard', async function() {
const compiler = new Compiler();
const compiler = new KmnCompiler();
const callbacks = new TestCompilerCallbacks();
assert(await compiler.init());
assert(await compiler.init(callbacks));
assert(compiler.verifyInitialized());
const fixtureName = baselineDir + 'k_000___null_keyboard.kmx';
const infile = baselineDir + 'k_000___null_keyboard.kmn';
const outfile = __dirname + '/k_000___null_keyboard.kmx';
assert(compiler.run(infile, outfile, callbacks, {saveDebug: true, shouldAddCompilerVersion: false}));
assert(compiler.run(infile, outfile, {saveDebug: true, shouldAddCompilerVersion: false}));
assert(fs.existsSync(outfile));
const outfileData = fs.readFileSync(outfile);
@ -49,9 +65,10 @@ describe('Compiler class', function() {
// Note, above test case is essentially a subset of this one, but will leave both because
// the basic keyboard test is slightly simpler to read
it('should build all baseline fixtures', async function() {
const compiler = new Compiler();
const compiler = new KmnCompiler();
const callbacks = new TestCompilerCallbacks();
assert(await compiler.init());
assert(await compiler.init(callbacks));
assert(compiler.verifyInitialized());
const files = fs.readdirSync(baselineDir);
for(let file of files) {
@ -60,7 +77,7 @@ describe('Compiler class', function() {
const infile = baselineDir + file.replace(/x$/, 'n');
const outfile = __dirname + '/' + file;
assert(compiler.run(infile, outfile, callbacks, {saveDebug: true, shouldAddCompilerVersion: false}));
assert(compiler.run(infile, outfile, {saveDebug: true, shouldAddCompilerVersion: false}));
assert(fs.existsSync(outfile));
const outfileData = fs.readFileSync(outfile);
@ -69,6 +86,5 @@ describe('Compiler class', function() {
assert.deepEqual(outfileData, fixtureData);
}
}
});
});
});

View file

@ -0,0 +1,66 @@
import 'mocha';
import { assert } from 'chai';
import { KmnCompiler } from '../src/main.js';
import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers';
import { CompilerMessages } from '../src/compiler/messages.js';
import { compilerErrorFormatCode } from '@keymanapp/common-types';
describe('Compiler UnicodeSet function', function() {
it('should start', async function() {
const compiler = new KmnCompiler();
const callbacks = new TestCompilerCallbacks();
assert(await compiler.init(callbacks));
assert(compiler.verifyInitialized());
});
it('should compile a basic uset', async function() {
const compiler = new KmnCompiler();
const callbacks = new TestCompilerCallbacks();
assert(await compiler.init(callbacks));
assert(compiler.verifyInitialized());
const pat = "[abc]";
const set = compiler.parseUnicodeSet(pat, 23);
assert(set.length === 1);
assert(set.ranges[0][0] === 'a'.charCodeAt(0));
assert(set.ranges[0][1] === 'c'.charCodeAt(0));
});
it('should compile a more complex uset', async function() {
const compiler = new KmnCompiler();
const callbacks = new TestCompilerCallbacks();
assert(await compiler.init(callbacks));
assert(compiler.verifyInitialized());
const pat = "[[🙀A-C]-[CB]]";
const set = compiler.parseUnicodeSet(pat, 23);
assert.equal(set.length, 2);
assert.equal(set.ranges[0][0], 'A'.charCodeAt(0));
assert.equal(set.ranges[0][1], 'A'.charCodeAt(0));
assert.equal(set.ranges[1][0], 0x1F640);
assert.equal(set.ranges[1][1], 0x1F640);
});
it('should fail in various ways', async function() {
const compiler = new KmnCompiler();
const callbacks = new TestCompilerCallbacks();
assert(await compiler.init(callbacks));
assert(compiler.verifyInitialized());
// map from string to failing error
const failures = {
'[:Adlm:]': CompilerMessages.ERROR_UnicodeSetHasProperties, // what it saye
'[acegik]': CompilerMessages.FATAL_UnicodeSetOutOfRange, // 6 ranges, allocated 1
'[[\\p{Mn}]&[A-Z]]': CompilerMessages.ERROR_UnicodeSetHasProperties,
'[abc{def}]': CompilerMessages.ERROR_UnicodeSetHasStrings,
'[[]': CompilerMessages.ERROR_UnicodeSetSyntaxError,
};
for(const [pat, expected] of Object.entries(failures)) {
callbacks.clear();
assert.notOk(compiler.parseUnicodeSet(pat, 1));
assert.equal(callbacks.messages.length, 1);
const firstMessage = callbacks.messages[0];
const code = firstMessage.code;
assert.equal(code, expected, `${compilerErrorFormatCode(code)}${compilerErrorFormatCode(expected)} got ${firstMessage.message} for ${pat}`);
}
});
});

View file

@ -1,6 +1,6 @@
export default interface CompilerOptions {
export interface CompilerOptions {
/**
* Add debug information to the .kmx file when compiling
*/

View file

@ -1,5 +1,5 @@
import { LDMLKeyboardXMLSourceFileReader, LDMLKeyboard, KMXPlus, CompilerCallbacks, LDMLKeyboardTestDataXMLSourceFile } from '@keymanapp/common-types';
import CompilerOptions from './compiler-options.js';
import { CompilerOptions } from './compiler-options.js';
import { CompilerMessages } from './messages.js';
import { BkspCompiler, FinlCompiler, TranCompiler } from './tran.js';
import { DispCompiler } from './disp.js';
@ -28,7 +28,7 @@ const SECTION_COMPILERS = [
VkeyCompiler,
];
export default class Compiler {
export class LdmlKeyboardCompiler {
private readonly callbacks: CompilerCallbacks;
// private readonly options: CompilerOptions; // not currently used

View file

@ -1,19 +1,19 @@
import { CompilerCallbacks, VisualKeyboard, LDMLKeyboard, TouchLayoutFileWriter } from "@keymanapp/common-types";
import CompilerOptions from "./compiler-options.js";
import { CompilerOptions } from "./compiler-options.js";
import { TouchLayoutCompiler } from "./touch-layout-compiler.js";
import VisualKeyboardCompiler from "./visual-keyboard-compiler.js";
import { LdmlKeyboardVisualKeyboardCompiler } from "./visual-keyboard-compiler.js";
const MINIMUM_KMW_VERSION = '16.0';
export interface KeymanWebCompilerOptions extends CompilerOptions {
export interface LdmlKeyboardKeymanWebCompilerOptions extends CompilerOptions {
};
export class KeymanWebCompiler {
private readonly options: KeymanWebCompilerOptions;
export class LdmlKeyboardKeymanWebCompiler {
private readonly options: LdmlKeyboardKeymanWebCompilerOptions;
private readonly nl: string;
private readonly tab: string;
constructor(private callbacks: CompilerCallbacks, options?: KeymanWebCompilerOptions) {
constructor(private callbacks: CompilerCallbacks, options?: LdmlKeyboardKeymanWebCompilerOptions) {
this.options = { ...options };
this.nl = this.options.debug ? "\n" : '';
this.tab = this.options.debug ? " " : '';
@ -21,7 +21,7 @@ export class KeymanWebCompiler {
public compileVisualKeyboard(source: LDMLKeyboard.LDMLKeyboardXMLSourceFile) {
const nl = this.nl, tab = this.tab;
const vkc = new VisualKeyboardCompiler();
const vkc = new LdmlKeyboardVisualKeyboardCompiler();
const vk: VisualKeyboard.VisualKeyboard = vkc.compile(source);
let result =

View file

@ -1,12 +1,12 @@
import { KMX, KMXPlus } from '@keymanapp/common-types';
import CompilerOptions from "./compiler-options.js";
import { CompilerOptions } from "./compiler-options.js";
import KEYMAN_VERSION from "@keymanapp/keyman-version";
import KMXPlusData = KMXPlus.KMXPlusData;
import KMXFile = KMX.KMXFile;
import KEYBOARD = KMX.KEYBOARD;
export default class KMXPlusMetadataCompiler {
export class KMXPlusMetadataCompiler {
/**
* Look for metadata fields in the KMXPlus data and copy them
* through to the relevant KMX stores

View file

@ -1,6 +1,6 @@
import { Constants, VisualKeyboard, LDMLKeyboard } from "@keymanapp/common-types";
export default class VisualKeyboardCompiler {
export class LdmlKeyboardVisualKeyboardCompiler {
public compile(source: LDMLKeyboard.LDMLKeyboardXMLSourceFile): VisualKeyboard.VisualKeyboard {
let result = new VisualKeyboard.VisualKeyboard();

View file

@ -1,10 +1,10 @@
export { default as Compiler } from './compiler/compiler.js';
export { KeymanWebCompiler } from './compiler/keymanweb-compiler.js';
export { default as VisualKeyboardCompiler } from './compiler/visual-keyboard-compiler.js';
export { LdmlKeyboardCompiler } from './compiler/compiler.js';
export { LdmlKeyboardKeymanWebCompiler } from './compiler/keymanweb-compiler.js';
export { LdmlKeyboardVisualKeyboardCompiler } from './compiler/visual-keyboard-compiler.js';
export { TouchLayoutCompiler } from './compiler/touch-layout-compiler.js';
export { default as CompilerOptions } from './compiler/compiler-options.js';
export { CompilerOptions } from './compiler/compiler-options.js';
export { CompilerMessages } from './compiler/messages.js';
export { default as KMXPlusMetadataCompiler } from './compiler/metadata-compiler.js';
export { KMXPlusMetadataCompiler } from './compiler/metadata-compiler.js';
export { KMXBuilder } from "@keymanapp/common-types";

View file

@ -6,11 +6,11 @@ import * as path from 'path';
import { fileURLToPath } from 'url';
import { SectionCompiler } from '../../src/compiler/section-compiler.js';
import { KMXPlus, LDMLKeyboardXMLSourceFileReader, VisualKeyboard, CompilerEvent, LDMLKeyboardTestDataXMLSourceFile } from '@keymanapp/common-types';
import Compiler from '../../src/compiler/compiler.js';
import { LdmlKeyboardCompiler } from '../../src/compiler/compiler.js';
import { assert } from 'chai';
import KMXPlusMetadataCompiler from '../../src/compiler/metadata-compiler.js';
import CompilerOptions from '../../src/compiler/compiler-options.js';
import VisualKeyboardCompiler from '../../src/compiler/visual-keyboard-compiler.js';
import { KMXPlusMetadataCompiler } from '../../src/compiler/metadata-compiler.js';
import { CompilerOptions } from '../../src/compiler/compiler-options.js';
import { LdmlKeyboardVisualKeyboardCompiler } from '../../src/compiler/visual-keyboard-compiler.js';
import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers';
import KMXPlusFile = KMXPlus.KMXPlusFile;
@ -76,13 +76,13 @@ export function loadSectionFixture(compilerClass: typeof SectionCompiler, filena
}
export function loadTestdata(inputFilename: string, options: CompilerOptions) : LDMLKeyboardTestDataXMLSourceFile {
const k = new Compiler(compilerTestCallbacks, options);
const k = new LdmlKeyboardCompiler(compilerTestCallbacks, options);
const source = k.loadTestData(inputFilename);
return source;
}
export function compileKeyboard(inputFilename: string, options: CompilerOptions): KMXPlusFile {
const k = new Compiler(compilerTestCallbacks, options);
const k = new LdmlKeyboardCompiler(compilerTestCallbacks, options);
const source = k.load(inputFilename);
checkMessages();
assert.isNotNull(source, 'k.load should not have returned null');
@ -103,7 +103,7 @@ export function compileKeyboard(inputFilename: string, options: CompilerOptions)
}
export function compileVisualKeyboard(inputFilename: string, options: CompilerOptions): VisualKeyboard.VisualKeyboard {
const k = new Compiler(compilerTestCallbacks, options);
const k = new LdmlKeyboardCompiler(compilerTestCallbacks, options);
const source = k.load(inputFilename);
checkMessages();
assert.isNotNull(source, 'k.load should not have returned null');
@ -112,9 +112,9 @@ export function compileVisualKeyboard(inputFilename: string, options: CompilerOp
checkMessages();
assert.isTrue(valid, 'k.validate should not have failed');
const vk = (new VisualKeyboardCompiler()).compile(source);
const vk = (new LdmlKeyboardVisualKeyboardCompiler()).compile(source);
checkMessages();
assert.isNotNull(vk, 'VisualKeyboardCompiler.compile should not have returned null');
assert.isNotNull(vk, 'LdmlKeyboardVisualKeyboardCompiler.compile should not have returned null');
return vk;
}

View file

@ -1,11 +1,11 @@
import 'mocha';
import { assert } from 'chai';
import { checkMessages, compilerTestCallbacks, makePathToFixture } from './helpers/index.js';
import { KeymanWebCompiler } from '../src/compiler/keymanweb-compiler.js';
import Compiler from '../src/compiler/compiler.js';
import { LdmlKeyboardKeymanWebCompiler } from '../src/compiler/keymanweb-compiler.js';
import { LdmlKeyboardCompiler } from '../src/compiler/compiler.js';
import * as fs from 'fs';
describe('KeymanWebCompiler', function() {
describe('LdmlKeyboardKeymanWebCompiler', function() {
it('should build a .js file', async function() {
// Let's build basic.xml
@ -16,7 +16,7 @@ describe('KeymanWebCompiler', function() {
// Load input data; we'll use the LDML keyboard compiler loader to save us
// effort here
const k = new Compiler(compilerTestCallbacks, {debug: true, addCompilerVersion: false});
const k = new LdmlKeyboardCompiler(compilerTestCallbacks, {debug: true, addCompilerVersion: false});
const source = k.load(inputFilename);
checkMessages();
assert.isNotNull(source, 'k.load should not have returned null');
@ -27,7 +27,7 @@ describe('KeymanWebCompiler', function() {
assert.isTrue(valid, 'k.validate should not have failed');
// Actual test: compile to javascript
const jsCompiler = new KeymanWebCompiler(compilerTestCallbacks, {debug: true});
const jsCompiler = new LdmlKeyboardKeymanWebCompiler(compilerTestCallbacks, {debug: true});
const output = jsCompiler.compile('basic.xml', source);
assert.isNotNull(output);
@ -36,7 +36,7 @@ describe('KeymanWebCompiler', function() {
assert.strictEqual(output, outputFixture);
// Second test: compile to javascript without debug formatting
const jsCompilerNoDebug = new KeymanWebCompiler(compilerTestCallbacks, {debug: false});
const jsCompilerNoDebug = new LdmlKeyboardKeymanWebCompiler(compilerTestCallbacks, {debug: false});
const outputNoDebug = jsCompilerNoDebug.compile('basic.xml', source);
assert.isNotNull(outputNoDebug);

View file

@ -28,7 +28,7 @@ builder_describe "Build Keyman kmc Package Compiler module" \
"--dry-run,-n don't actually publish, just dry run"
builder_describe_outputs \
configure /node_modules \
build /developer/src/kmc-package/build/src/kmp-compiler.js
build /developer/src/kmc-package/build/src/main.js
builder_parse "$@"
#-------------------------------------------------------------------------------------------------------------------

View file

@ -4,7 +4,8 @@ import KEYMAN_VERSION from "@keymanapp/keyman-version";
import { CompilerCallbacks, KvkFile } from '@keymanapp/common-types';
import { CompilerMessages } from './messages.js';
import { KmpJsonFile, KpsFile, KMX, KmxFileReader } from '@keymanapp/common-types';
import { KmpJsonFile, KpsFile } from '@keymanapp/common-types';
import { PackageVersionValidation } from './package-version-validation.js';
const FILEVERSION_KMP_JSON = '12.0';
@ -104,16 +105,16 @@ export class KmpCompiler {
//
if(kps.keyboards && kps.keyboards.keyboard) {
kmp.keyboards = this.arrayWrap(kps.keyboards.keyboard).map((keyboard: KpsFile.KpsFileKeyboard) => {
return {
displayFont: keyboard.displayFont ? this.callbacks.path.basename(keyboard.displayFont) : undefined,
oskFont: keyboard.oSKFont ? this.callbacks.path.basename(keyboard.oSKFont) : undefined,
name:keyboard.name,
id:keyboard.iD,
version:keyboard.version,
languages: this.kpsLanguagesToKmpLanguages(this.arrayWrap(keyboard.languages.language) as KpsFile.KpsFileLanguage[])
};
});
kmp.keyboards = this.arrayWrap(kps.keyboards.keyboard).map((keyboard: KpsFile.KpsFileKeyboard) => ({
displayFont: keyboard.displayFont ? this.callbacks.path.basename(keyboard.displayFont) : undefined,
oskFont: keyboard.oSKFont ? this.callbacks.path.basename(keyboard.oSKFont) : undefined,
name:keyboard.name,
id:keyboard.iD,
version:keyboard.version,
languages: keyboard.languages ?
this.kpsLanguagesToKmpLanguages(this.arrayWrap(keyboard.languages.language) as KpsFile.KpsFileLanguage[]) :
[]
}));
}
//
@ -121,20 +122,22 @@ export class KmpCompiler {
//
if(kps.lexicalModels && kps.lexicalModels.lexicalModel) {
kmp.lexicalModels = this.arrayWrap(kps.lexicalModels.lexicalModel).map((model: KpsFile.KpsFileLexicalModel) => {
return { name:model.name, id:model.iD, languages: this.kpsLanguagesToKmpLanguages(this.arrayWrap(model.languages.language) as KpsFile.KpsFileLanguage[]) }
});
kmp.lexicalModels = this.arrayWrap(kps.lexicalModels.lexicalModel).map((model: KpsFile.KpsFileLexicalModel) => ({
name:model.name,
id:model.iD,
languages: model.languages ?
this.kpsLanguagesToKmpLanguages(this.arrayWrap(model.languages.language) as KpsFile.KpsFileLanguage[]) : []
}));
}
//
// FollowKeyboardVersion support
// Verify version metadata; doing this in the transform
// while we have access to the .kps metadata, and keeping the
//
if(kps.options?.followKeyboardVersion !== undefined) {
kmp.info.version = {
description: this.extractKeyboardVersionFromKmx(kpsFilename, kmp)
};
// TODO: compare the extracted version with other keyboards in the package
const versionValidator = new PackageVersionValidation(this.callbacks);
if(!versionValidator.validateAndUpdateVersions(kpsFilename, kps, kmp)) {
return null;
}
//
@ -195,67 +198,7 @@ export class KmpCompiler {
return language.map((element) => { return { name: element._, id: element.$.ID } });
};
private extractKeyboardVersionFromKmx(kpsFilename: string, kmp: KmpJsonFile.KmpJsonFile) {
// The DEFAULT_VERSION used to be '1.0', but we now use '0.0' to allow
// pre-release 0.x keyboards to be considered later than a keyboard without
// any version metadata at all.
const DEFAULT_VERSION = '0.0';
// Note: there is often version metadata in the .kps <Keyboard> element, but
// we don't read from the metadata because we want to ensure we have the
// most up-to-date keyboard version data here, from the compiled keyboard.
// Lexical model packages do not allow FollowKeyboardVersion
if(kmp.lexicalModels && kmp.lexicalModels.length) {
this.callbacks.reportMessage(CompilerMessages.Error_FollowKeyboardVersionNotAllowedForModelPackages());
return DEFAULT_VERSION;
}
if(!kmp.keyboards || !kmp.keyboards.length) {
this.callbacks.reportMessage(CompilerMessages.Warn_FollowKeyboardVersionButNoKeyboards());
return DEFAULT_VERSION;
}
// Reset the keyboard version to the default in the kmp.json metadata, for
// warning/failure code paths in this file
kmp.keyboards[0].version = DEFAULT_VERSION;
const file = kmp.files.find(file => this.callbacks.path.basename(file.name, '.kmx') == kmp.keyboards[0].id);
if(!file) {
this.callbacks.reportMessage(CompilerMessages.Error_KeyboardFileNotFound({id:kmp.keyboards[0].id}));
return DEFAULT_VERSION;
}
const filename = this.callbacks.resolveFilename(kpsFilename, file.name);
if(!this.callbacks.fs.existsSync(filename)) {
// The zip phase will emit an error later if the file is missing, so
// we can just bail cleanly here
// console.debug(`The file ${filename} was not found`);
return DEFAULT_VERSION;
}
//
// load the .kmx and extract the version number
//
const kmxFileData = this.callbacks.loadFile(filename);
const kmxReader: KmxFileReader = new KmxFileReader();
const kmx: KMX.KEYBOARD = kmxReader.read(kmxFileData);
if(!kmx) {
// The file couldn't be read, it might be invalid or locked
this.callbacks.reportMessage(CompilerMessages.Error_KeyboardFileNotValid({filename}));
return DEFAULT_VERSION;
}
const store = kmx.stores.find(store => store.dwSystemID == KMX.KMXFile.TSS_KEYBOARDVERSION);
if(!store) {
// We have no version number store, so use default version
this.callbacks.reportMessage(CompilerMessages.Warn_KeyboardFileHasNoKeyboardVersion({filename}));
return DEFAULT_VERSION;
}
kmp.keyboards[0].version = store.dpString;
return store.dpString;
}
private stripUndefined(o: any) {
for(const key in o) {

View file

@ -1,7 +1,7 @@
import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m } from "@keymanapp/common-types";
const Namespace = CompilerErrorNamespace.PackageCompiler;
// const SevInfo = CompilerErrorSeverity.Info | Namespace;
const SevInfo = CompilerErrorSeverity.Info | Namespace;
// const SevHint = CompilerErrorSeverity.Hint | Namespace;
const SevWarn = CompilerErrorSeverity.Warn | Namespace;
const SevError = CompilerErrorSeverity.Error | Namespace;
@ -31,28 +31,28 @@ export class CompilerMessages {
`FollowKeyboardVersion is not allowed in model packages`);
static ERROR_FollowKeyboardVersionNotAllowedForModelPackages = SevError | 0x0006;
static Warn_FollowKeyboardVersionButNoKeyboards = () => m(this.WARN_FollowKeyboardVersionButNoKeyboards,
static Error_FollowKeyboardVersionButNoKeyboards = () => m(this.ERROR_FollowKeyboardVersionButNoKeyboards,
`FollowKeyboardVersion is set, but the package contains no keyboards`);
static WARN_FollowKeyboardVersionButNoKeyboards = SevWarn | 0x0007;
static ERROR_FollowKeyboardVersionButNoKeyboards = SevError | 0x0007;
static Error_KeyboardFileNotFound = (o:{id:string}) => m(this.ERROR_KeyboardFileNotFound,
static Error_KeyboardContentFileNotFound = (o:{id:string}) => m(this.ERROR_KeyboardContentFileNotFound,
`Keyboard ${o.id} was listed in <Keyboards> but a corresponding .kmx file was not found in <Files>`);
static ERROR_KeyboardFileNotFound = SevError | 0x0008;
static ERROR_KeyboardContentFileNotFound = SevError | 0x0008;
static Error_KeyboardFileNotValid = (o:{filename:string}) => m(this.ERROR_KeyboardFileNotValid,
`Keyboard file ${o.filename} is not a valid .kmx file`);
static ERROR_KeyboardFileNotValid = SevError | 0x0009;
static Warn_KeyboardFileHasNoKeyboardVersion = (o:{filename:string}) => m(this.WARN_KeyboardFileHasNoKeyboardVersion,
`Keyboard file ${o.filename} has no &KeyboardVersion store`);
static WARN_KeyboardFileHasNoKeyboardVersion = SevWarn | 0x000A;
static Info_KeyboardFileHasNoKeyboardVersion = (o:{filename:string}) => m(this.INFO_KeyboardFileHasNoKeyboardVersion,
`Keyboard file ${o.filename} has no &KeyboardVersion store, using default '0.0'`);
static INFO_KeyboardFileHasNoKeyboardVersion = SevInfo | 0x000A;
static Error_PackageCannotContainBothModelsAndKeyboards = () => m(this.ERROR_PackageCannotContainBothModelsAndKeyboards,
`The package contains both lexical models and keyboards, which is not permitted.`);
static ERROR_PackageCannotContainBothModelsAndKeyboards = SevError | 0x000B;
static Warn_PackageShouldNotRepeatLanguages = (o:{resourceType: string, id: string, tag: string}) => m(this.WARN_PackageShouldNotRepeatLanguages,
`The ${o.resourceType} ${o.id} has a repeated language "${o.tag}".`);
static Warn_PackageShouldNotRepeatLanguages = (o:{resourceType: string, id: string, minimalTag: string, firstTag: string, secondTag: string}) => m(this.WARN_PackageShouldNotRepeatLanguages,
`Two language tags in ${o.resourceType} ${o.id}, '${o.firstTag}' and '${o.secondTag}', reduce to the same minimal tag '${o.minimalTag}'.`);
static WARN_PackageShouldNotRepeatLanguages = SevWarn | 0x000C;
static Warn_PackageNameDoesNotFollowLexicalModelConventions = (o:{filename: string}) => m(this.WARN_PackageNameDoesNotFollowLexicalModelConventions,
@ -74,5 +74,41 @@ export class CompilerMessages {
static Error_PackageNameCannotBeBlank = () => m(this.ERROR_PackageNameCannotBeBlank,
`Package name cannot be an empty string.`);
static ERROR_PackageNameCannotBeBlank = SevError | 0x0010;
static Error_KeyboardFileNotFound = (o:{filename:string}) => m(this.ERROR_KeyboardFileNotFound,
`Keyboard file ${o.filename} was not found. Has it been compiled?`);
static ERROR_KeyboardFileNotFound = SevError | 0x0011;
static Warn_KeyboardVersionsDoNotMatch = (o: {keyboard:string, version:string, firstKeyboard:string, firstVersion:string}) => m(this.WARN_KeyboardVersionsDoNotMatch,
`Keyboard ${o.keyboard} version ${o.version} does not match keyboard ${o.firstKeyboard} version ${o.firstVersion}.`);
static WARN_KeyboardVersionsDoNotMatch = SevWarn | 0x0012;
static Warn_KeyboardVersionsDoNotMatchPackageVersion = (o: {keyboard:string, keyboardVersion: string, packageVersion: string}) => m(this.WARN_KeyboardVersionsDoNotMatchPackageVersion,
`Keyboard ${o.keyboard} version ${o.keyboardVersion} does not match package version ${o.packageVersion}.`);
static WARN_KeyboardVersionsDoNotMatchPackageVersion = SevWarn | 0x0013;
static Error_LanguageTagIsNotValid = (o: {resourceType: string, id:string, lang:string, e:any}) => m(this.ERROR_LanguageTagIsNotValid,
`Language tag '${o.lang}' in ${o.resourceType} ${o.id} is invalid.`);
static ERROR_LanguageTagIsNotValid = SevError | 0x0014;
static Warn_LanguageTagIsNotMinimal = (o: {resourceType: string, id:string, actual:string, expected:string}) => m(this.WARN_LanguageTagIsNotMinimal,
`Language tag '${o.actual}' in ${o.resourceType} ${o.id} is not minimal, and should be '${o.expected}'.`);
static WARN_LanguageTagIsNotMinimal = SevWarn | 0x0015;
static Error_MustHaveAtLeastOneLanguage = (o:{resourceType:string, id:string}) => m(this.ERROR_MustHaveAtLeastOneLanguage,
`The ${o.resourceType} ${o.id} must have at least one language specified.`);
static ERROR_MustHaveAtLeastOneLanguage = SevError | 0x0016;
static Warn_RedistFileShouldNotBeInPackage = (o:{filename:string}) => m(this.WARN_RedistFileShouldNotBeInPackage,
`The Keyman system file '${o.filename}' should not be compiled into the package.`);
static WARN_RedistFileShouldNotBeInPackage = SevWarn | 0x0017;
static Warn_DocFileDangerous = (o:{filename:string}) => m(this.WARN_DocFileDangerous,
`Microsoft Word .doc or .docx files ('${o.filename}') are not portable. You should instead use HTML or PDF format.`);
static WARN_DocFileDangerous = SevWarn | 0x0018;
static Error_PackageMustContainAModelOrAKeyboard = () => m(this.ERROR_PackageMustContainAModelOrAKeyboard,
`Package must contain a lexical model or a keyboard.`);
static ERROR_PackageMustContainAModelOrAKeyboard = SevError | 0x0019;
}

View file

@ -1,5 +1,6 @@
import { KmpJsonFile, CompilerCallbacks } from '@keymanapp/common-types';
import { CompilerMessages } from './messages.js';
import { keymanEngineForWindowsFiles, keymanForWindowsInstallerFiles, keymanForWindowsRedistFiles } from './redist-files.js';
// const SLexicalModelExtension = '.model.js';
@ -41,16 +42,38 @@ export class PackageValidation {
return true;
}
private checkForDuplicatedLanguages(resourceType: 'keyboard'|'model', id: string, languages: KmpJsonFile.KmpJsonFileLanguage[]) {
let tags: {[index:string]: boolean} = {};
private checkForDuplicatedOrNonMinimalLanguages(resourceType: 'keyboard'|'model', id: string, languages: KmpJsonFile.KmpJsonFileLanguage[]): boolean {
let minimalTags: {[tag: string]: string} = {};
if(languages.length == 0) {
this.callbacks.reportMessage(CompilerMessages.Error_MustHaveAtLeastOneLanguage({resourceType, id}));
return false;
}
for(let lang of languages) {
const langTag = lang.id.toLowerCase();
if(tags[langTag]) {
this.callbacks.reportMessage(CompilerMessages.Warn_PackageShouldNotRepeatLanguages({resourceType:resourceType, id:id, tag:lang.id}));
} else {
tags[langTag] = true;
let locale;
try {
locale = new Intl.Locale(lang.id);
} catch(e: any) {
this.callbacks.reportMessage(CompilerMessages.Error_LanguageTagIsNotValid({resourceType, id, lang: lang.id, e}));
return false;
}
const minimalTag = locale.minimize().toString();
if(minimalTag.toLowerCase() !== lang.id.toLowerCase()) {
this.callbacks.reportMessage(CompilerMessages.Warn_LanguageTagIsNotMinimal({resourceType, id, actual: lang.id, expected: minimalTag}));
}
if(minimalTags[minimalTag]) {
this.callbacks.reportMessage(CompilerMessages.Warn_PackageShouldNotRepeatLanguages({resourceType, id, minimalTag, firstTag: lang.id, secondTag: minimalTags[minimalTag]}));
}
else {
minimalTags[minimalTag] = lang.id;
}
}
return true;
}
private checkForModelsAndKeyboardsInSamePackage(kmpJson: KmpJsonFile.KmpJsonFile): boolean {
@ -59,6 +82,16 @@ export class PackageValidation {
return false;
}
if(!kmpJson.lexicalModels?.length && !kmpJson.keyboards?.length) {
// Note: we require at least 1 keyboard or model in the package. This may
// change in the future if we start to use packages to distribute, e.g.
// localizations or themes.
this.callbacks.reportMessage(CompilerMessages.Error_PackageMustContainAModelOrAKeyboard());
return false;
}
return true;
}
@ -74,7 +107,9 @@ export class PackageValidation {
}
for(let model of kmpJson.lexicalModels) {
this.checkForDuplicatedLanguages('model', model.id, model.languages);
if(!this.checkForDuplicatedOrNonMinimalLanguages('model', model.id, model.languages)) {
return false;
}
}
return true;
@ -92,7 +127,9 @@ export class PackageValidation {
}
for(let keyboard of kmpJson.keyboards) {
this.checkForDuplicatedLanguages('keyboard', keyboard.id, keyboard.languages);
if(!this.checkForDuplicatedOrNonMinimalLanguages('keyboard', keyboard.id, keyboard.languages)) {
return false;
}
}
return true;
@ -116,6 +153,23 @@ export class PackageValidation {
this.callbacks.reportMessage(CompilerMessages.Warn_FileInPackageDoesNotFollowFilenameConventions({filename}));
}
if(!this.checkIfContentFileIsDangerous(file)) {
return false;
}
return true;
}
private checkIfContentFileIsDangerous(file: KmpJsonFile.KmpJsonFileContentFile): boolean {
let filename = this.callbacks.path.basename(file.name).toLowerCase();
if(keymanForWindowsInstallerFiles.includes(filename) ||
keymanForWindowsRedistFiles.includes(filename) ||
keymanEngineForWindowsFiles.includes(filename)) {
this.callbacks.reportMessage(CompilerMessages.Warn_RedistFileShouldNotBeInPackage({filename}));
}
if(filename.match(/\.doc(x?)$/)) {
this.callbacks.reportMessage(CompilerMessages.Warn_DocFileDangerous({filename}));
}
return true;
}

View file

@ -0,0 +1,139 @@
import { KmpJsonFile, CompilerCallbacks, KpsFile, KmxFileReader, KMX } from '@keymanapp/common-types';
import { CompilerMessages } from './messages.js';
export class PackageVersionValidation {
constructor(private callbacks: CompilerCallbacks) {}
/**
* Verifies version information in corresponding keyboards and updates kmpJson
* metadata as the version information can be out of sync in the .kps file
* after updating a contained keyboard.
* @param kpsFilename
* @param kps
* @param kmp
* @returns
*/
public validateAndUpdateVersions(kpsFilename: string, kps: KpsFile.KpsFile, kmp: KmpJsonFile.KmpJsonFile) {
const followKeyboardVersion = kps.options?.followKeyboardVersion !== undefined;
if(followKeyboardVersion) {
if(!this.checkFollowKeyboardVersion(kps, kmp)) {
return false;
}
}
if(!kmp.keyboards) {
// Lexical models don't have version metadata; only their packages.
return true;
}
let result = true;
// We now know we have at least one keyboard in the package
for(let keyboard of kmp.keyboards) {
result = this.updateKeyboardVersionFromKmx(kpsFilename, kmp, keyboard) && result;
if(result) {
if(kmp.keyboards[0].version !== keyboard.version) {
this.callbacks.reportMessage(CompilerMessages.Warn_KeyboardVersionsDoNotMatch({
keyboard:keyboard.id,
version:keyboard.version,
firstKeyboard:kmp.keyboards[0].id,
firstVersion:kmp.keyboards[0].version
}));
}
}
}
if(result) {
if(followKeyboardVersion) {
kmp.info.version.description = kmp.keyboards[0].version;
}
else if(kmp.info.version?.description != kmp.keyboards[0].version) {
// Only need to compare against first keyboard as we compare keyboards above
this.callbacks.reportMessage(CompilerMessages.Warn_KeyboardVersionsDoNotMatchPackageVersion({
keyboard: kmp.keyboards[0].id,
keyboardVersion: kmp.keyboards[0].version,
packageVersion: kmp.info.version?.description
}));
}
}
return result;
}
private checkFollowKeyboardVersion(kps: KpsFile.KpsFile, kmp: KmpJsonFile.KmpJsonFile) {
// Lexical model packages do not allow FollowKeyboardVersion
if(kmp.lexicalModels && kmp.lexicalModels.length) {
this.callbacks.reportMessage(CompilerMessages.Error_FollowKeyboardVersionNotAllowedForModelPackages());
return false;
}
if(!kmp.keyboards || !kmp.keyboards.length) {
this.callbacks.reportMessage(CompilerMessages.Error_FollowKeyboardVersionButNoKeyboards());
return false;
}
return true;
}
private updateKeyboardVersionFromKmx(
kpsFilename: string,
kmp: KmpJsonFile.KmpJsonFile,
keyboard: KmpJsonFile.KmpJsonFileKeyboard
): boolean {
// The DEFAULT_VERSION used to be '1.0', but we now use '0.0' to allow
// pre-release 0.x keyboards to be considered later than a keyboard without
// any version metadata at all.
const DEFAULT_VERSION = '0.0';
// Note: there is often version metadata in the .kps <Keyboard> element, but
// we don't read from the metadata because we want to ensure we have the
// most up-to-date keyboard version data here, from the compiled keyboard.
// Reset the keyboard version to the default in the kmp.json metadata, for
// warning/failure code paths in this file
keyboard.version = DEFAULT_VERSION;
const file = kmp.files.find(file => this.callbacks.path.basename(file.name, '.kmx') == keyboard.id);
if(!file) {
this.callbacks.reportMessage(CompilerMessages.Error_KeyboardContentFileNotFound({id:keyboard.id}));
return false;
}
const filename = this.callbacks.resolveFilename(kpsFilename, file.name);
if(!this.callbacks.fs.existsSync(filename)) {
this.callbacks.reportMessage(CompilerMessages.Error_KeyboardFileNotFound({filename}));
return false;
}
//
// load the .kmx and extract the version number
//
let kmxFileData;
try {
kmxFileData = this.callbacks.loadFile(filename);
} catch(e) {
this.callbacks.reportMessage(CompilerMessages.Error_FileCouldNotBeRead({filename, e}));
return false;
}
const kmxReader: KmxFileReader = new KmxFileReader();
const kmx: KMX.KEYBOARD = kmxReader.read(kmxFileData);
if(!kmx) {
// The file couldn't be read, it might not be a .kmx file
this.callbacks.reportMessage(CompilerMessages.Error_KeyboardFileNotValid({filename}));
return false;
}
const store = kmx.stores.find(store => store.dwSystemID == KMX.KMXFile.TSS_KEYBOARDVERSION);
if(!store) {
// We have no version number store, so use default version
this.callbacks.reportMessage(CompilerMessages.Info_KeyboardFileHasNoKeyboardVersion({filename}));
return true;
}
keyboard.version = store.dpString;
return true;
}
}

View file

@ -0,0 +1,64 @@
/**
* This is a set of known redistributable files for Keyman for Windows that
* should not be included in packages. It is not critical that this list matches
* the current deployment; it is just for warning against accidental inclusion
* of these files by package authors. Some redistributable files have been
* intentionally excluded because they could legitimately be a different file
* with the same name.
*
* This matches behaviour from the legacy package compiler; we may want to
* reconsider how this is done in the future.
*
* These lists have been constructed from 17.0.109 alpha build. Filenames
* intentionally in lower case.
*/
export const
keymanForWindowsInstallerFiles: string[] = [
'keymandesktop.msi',
'keymanengine.msm'
];
export const
keymanEngineForWindowsFiles: string[] = [
'base.xslt',
'crashpad_handler.exe',
'keyman-debug-etw.man',
'keyman.exe',
'keyman32.dll',
'keyman64.dll',
'keymanmc.dll',
'keymanx64.exe',
'kmcomapi.dll',
'kmcomapi.x64.dll',
'kmrefresh.x64.exe',
'kmrefresh.x86.exe',
'kmtip.dll',
'kmtip64.dll',
'mcompile.exe',
'sentry.dll',
'sentry.x64.dll',
'si_browsers.xslt',
'si_fonts.xslt',
'si_hookdlls.xslt',
'si_keyman.xslt',
'si_language.xslt',
'si_office.xslt',
'si_overview.xslt',
'si_processes.xslt',
'si_processes_x64.xslt',
'si_startup.xslt',
'tsysinfo.exe',
];
export const
keymanForWindowsRedistFiles: string[] = [
'desktop_resources.dll',
'keymandesktop.chm',
'kmbrowserhost.exe',
'kmconfig.exe',
'kmshell.exe',
'unicodedata.mdb',
];

View file

@ -17,6 +17,7 @@
</StartMenu>
<Info>
<Name URL="">Binary KVK File</Name>
<Version>1.3</Version>
</Info>
<Files>
<File>
@ -25,5 +26,21 @@
<CopyLocation>0</CopyLocation>
<FileType>.kvk</FileType>
</File>
<File>
<Name>../../invalid/khmer_angkor.kmx</Name>
<Description>Keyboard Khmer Angkor</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kmx</FileType>
</File>
</Files>
<Keyboards>
<Keyboard>
<Name>Khmer Angkor</Name>
<ID>khmer_angkor</ID>
<Version>1.3</Version>
<Languages>
<Language ID="KM">Khmer</Language>
</Languages>
</Keyboard>
</Keyboards>
</Package>

View file

@ -9,11 +9,11 @@
<Name URL="">SENĆOŦEN (Saanich Dialect) Lexical Model</Name>
<Copyright URL="">© 2019 National Research Council Canada</Copyright>
<Author URL="mailto:Eddie.Santos@nrc-cnrc.gc.ca">Eddie Antonio Santos</Author>
<Version>1.0.3</Version>
<Version>1.3</Version>
</Info>
<Files>
<File>
<Name>nokeyboardversion.kmx</Name>
<Name>khmer_angkor.kmx</Name>
<Description>Keyboard Khmer Angkor</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kmx</FileType>
@ -22,7 +22,7 @@
<Keyboards>
<Keyboard>
<Name>Khmer Angkor</Name>
<ID>nokeyboardversion</ID>
<ID>khmer_angkor</ID>
<Version>1.3</Version>
<Languages>
<Language ID="km">Central Khmer (Khmer, Cambodia)</Language>

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<Package>
<System>
<KeymanDeveloperVersion>15.0.266.0</KeymanDeveloperVersion>
<FileVersion>7.0</FileVersion>
</System>
<Info>
<Name URL="">SENĆOŦEN (Saanich Dialect) Lexical Model</Name>
<Copyright URL="">© 2019 National Research Council Canada</Copyright>
<Author URL="mailto:Eddie.Santos@nrc-cnrc.gc.ca">Eddie Antonio Santos</Author>
<Version>1.3</Version>
</Info>
<Files>
<File>
<Name>khmer_angkor.kmx</Name>
<Description>Keyboard Khmer Angkor</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kmx</FileType>
</File>
</Files>
<Keyboards>
<Keyboard>
<Name>Khmer Angkor</Name>
<ID>khmer_angkor</ID>
<Version>1.3</Version>
<Languages>
<!-- ERROR_LanguageTagIsNotValid -->
<Language ID="en-au-latn">English (Australian script) as spoken in Latin</Language>
</Languages>
</Keyboard>
</Keyboards>
</Package>

View file

@ -8,12 +8,12 @@
<Name URL="">Khmer Angkor</Name>
<Copyright URL="">© 2015-2022 SIL International</Copyright>
<Author URL="mailto:makara_sok@sil.org">Makara Sok</Author>
<Version URL=""></Version>
<Version URL="">1.3</Version>
<WebSite URL="https://keyman.com/keyboards/khmer_angkor">https://keyman.com/keyboards/khmer_angkor</WebSite>
</Info>
<Files>
<File>
<Name>nokeyboardversion.kmx</Name>
<Name>khmer_angkor.kmx</Name>
<Description>Keyboard Khmer Angkor</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kmx</FileType>
@ -39,7 +39,7 @@
<Keyboards>
<Keyboard>
<Name>Khmer Angkor</Name>
<ID>nokeyboardversion</ID>
<ID>khmer_angkor</ID>
<Version>1.3</Version>
<Languages>
<Language ID="km">Central Khmer (Khmer, Cambodia)</Language>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Package>
<System>
<KeymanDeveloperVersion>15.0.266.0</KeymanDeveloperVersion>
<FileVersion>7.0</FileVersion>
</System>
<Info>
<Name URL="">error_package_must_contain_a_model_or_a_keyboard</Name>
<Version>1.3</Version>
</Info>
<Files>
<File>
<!-- error_package_must_contain_a_model_or_a_keyboard. This is a common error:
to include a .kmn instead of a .kmx -->
<Name>khmer_angkor.kmn</Name>
<Description>Keyboard Khmer Angkor</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kmn</FileType>
</File>
</Files>
</Package>

View file

@ -9,11 +9,11 @@
<Name URL=""> </Name>
<Copyright URL="">© 2019 National Research Council Canada</Copyright>
<Author URL="mailto:Eddie.Santos@nrc-cnrc.gc.ca">Eddie Antonio Santos</Author>
<Version>1.0.3</Version>
<Version>1.3</Version>
</Info>
<Files>
<File>
<Name>nokeyboardversion.kmx</Name>
<Name>khmer_angkor.kmx</Name>
<Description>Keyboard Khmer Angkor</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kmx</FileType>
@ -22,7 +22,7 @@
<Keyboards>
<Keyboard>
<Name>Khmer Angkor</Name>
<ID>nokeyboardversion</ID>
<ID>khmer_angkor</ID>
<Version>1.3</Version>
<Languages>
<Language ID="km">Central Khmer (Khmer, Cambodia)</Language>

View file

@ -8,11 +8,11 @@
<!-- missing <Name/> -->
<Copyright URL="">© 2019 National Research Council Canada</Copyright>
<Author URL="mailto:Eddie.Santos@nrc-cnrc.gc.ca">Eddie Antonio Santos</Author>
<Version>1.0.3</Version>
<Version>1.3</Version>
</Info>
<Files>
<File>
<Name>nokeyboardversion.kmx</Name>
<Name>khmer_angkor.kmx</Name>
<Description>Keyboard Khmer Angkor</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kmx</FileType>
@ -21,7 +21,7 @@
<Keyboards>
<Keyboard>
<Name>Khmer Angkor</Name>
<ID>nokeyboardversion</ID>
<ID>khmer_angkor</ID>
<Version>1.3</Version>
<Languages>
<Language ID="km">Central Khmer (Khmer, Cambodia)</Language>

View file

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<Package>
<System>
<KeymanDeveloperVersion>15.0.266.0</KeymanDeveloperVersion>
<FileVersion>7.0</FileVersion>
</System>
<Options>
<ExecuteProgram></ExecuteProgram>
<ReadMeFile>readme.htm</ReadMeFile>
<GraphicFile>splash.gif</GraphicFile>
<MSIFileName></MSIFileName>
<MSIOptions></MSIOptions>
<FollowKeyboardVersion/>
</Options>
<StartMenu>
<Folder></Folder>
<Items/>
</StartMenu>
<Info>
<Name URL="">Khmer Angkor</Name>
<Copyright URL="">© 2015-2022 SIL International</Copyright>
<Author URL="mailto:makara_sok@sil.org">Makara Sok</Author>
<Version URL=""></Version>
<WebSite URL="https://keyman.com/keyboards/khmer_angkor">https://keyman.com/keyboards/khmer_angkor</WebSite>
</Info>
<Files>
<!-- missing .kmx triggers this warning: We're moving towards requiring .kmx
even for mobile-only keyboards, and have made the decision not to attempt
to parse .js for the kmp compiler, because it is too fragile. -->
<File>
<Name>..\build\khmer_angkor.js</Name>
<Description>File khmer_angkor.js</Description>
<CopyLocation>0</CopyLocation>
<FileType>.js</FileType>
</File>
<File>
<Name>..\build\khmer_angkor.kvk</Name>
<Description>File khmer_angkor.kvk</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kvk</FileType>
</File>
<File>
<Name>welcome\keyboard_layout.png</Name>
<Description>File keyboard_layout.png</Description>
<CopyLocation>0</CopyLocation>
<FileType>.png</FileType>
</File>
<File>
<Name>welcome\welcome.htm</Name>
<Description>File welcome.htm</Description>
<CopyLocation>0</CopyLocation>
<FileType>.htm</FileType>
</File>
<File>
<Name>..\shared\fonts\khmer\mondulkiri\FONTLOG.txt</Name>
<Description>File FONTLOG.txt</Description>
<CopyLocation>0</CopyLocation>
<FileType>.txt</FileType>
</File>
<File>
<Name>..\shared\fonts\khmer\mondulkiri\Mondulkiri-R.ttf</Name>
<Description>Font Khmer Mondulkiri</Description>
<CopyLocation>0</CopyLocation>
<FileType>.ttf</FileType>
</File>
<File>
<Name>..\shared\fonts\khmer\mondulkiri\OFL.txt</Name>
<Description>File OFL.txt</Description>
<CopyLocation>0</CopyLocation>
<FileType>.txt</FileType>
</File>
<File>
<Name>..\shared\fonts\khmer\mondulkiri\OFL-FAQ.txt</Name>
<Description>File OFL-FAQ.txt</Description>
<CopyLocation>0</CopyLocation>
<FileType>.txt</FileType>
</File>
<File>
<Name>welcome\KAK_Documentation_EN.pdf</Name>
<Description>File KAK_Documentation_EN.pdf</Description>
<CopyLocation>0</CopyLocation>
<FileType>.pdf</FileType>
</File>
<File>
<Name>welcome\KAK_Documentation_KH.pdf</Name>
<Description>File KAK_Documentation_KH.pdf</Description>
<CopyLocation>0</CopyLocation>
<FileType>.pdf</FileType>
</File>
<File>
<Name>readme.htm</Name>
<Description>File readme.htm</Description>
<CopyLocation>0</CopyLocation>
<FileType>.htm</FileType>
</File>
<File>
<Name>welcome\image002.png</Name>
<Description>File image002.png</Description>
<CopyLocation>0</CopyLocation>
<FileType>.png</FileType>
</File>
<File>
<Name>..\shared\fonts\khmer\busrakbd\khmer_busra_kbd.ttf</Name>
<Description>Font KhmerBusraKbd</Description>
<CopyLocation>0</CopyLocation>
<FileType>.ttf</FileType>
</File>
<File>
<Name>splash.gif</Name>
<Description>File splash.gif</Description>
<CopyLocation>0</CopyLocation>
<FileType>.gif</FileType>
</File>
</Files>
<Keyboards>
<Keyboard>
<Name>Khmer Angkor</Name>
<ID>khmer_angkor</ID>
<Version>1.3</Version>
<OSKFont>..\shared\fonts\khmer\busrakbd\khmer_busra_kbd.ttf</OSKFont>
<DisplayFont>..\shared\fonts\khmer\mondulkiri\Mondulkiri-R.ttf</DisplayFont>
<Languages>
<Language ID="km">Central Khmer (Khmer, Cambodia)</Language>
</Languages>
</Keyboard>
</Keyboards>
<Strings/>
</Package>

View file

@ -5,11 +5,6 @@
<FileVersion>7.0</FileVersion>
</System>
<Options>
<ExecuteProgram></ExecuteProgram>
<ReadMeFile>readme.htm</ReadMeFile>
<GraphicFile>splash.gif</GraphicFile>
<MSIFileName></MSIFileName>
<MSIOptions></MSIOptions>
<FollowKeyboardVersion/>
</Options>
<StartMenu>
@ -25,97 +20,18 @@
</Info>
<Files>
<File>
<Name>..\build\khmer_angkor.js</Name>
<Description>File khmer_angkor.js</Description>
<!-- File is missing -->
<Name>keyboardfilenotfound.kmx</Name>
<Description>Keyboard Khmer Angkor</Description>
<CopyLocation>0</CopyLocation>
<FileType>.js</FileType>
</File>
<File>
<Name>..\build\khmer_angkor.kvk</Name>
<Description>File khmer_angkor.kvk</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kvk</FileType>
</File>
<File>
<Name>welcome\keyboard_layout.png</Name>
<Description>File keyboard_layout.png</Description>
<CopyLocation>0</CopyLocation>
<FileType>.png</FileType>
</File>
<File>
<Name>welcome\welcome.htm</Name>
<Description>File welcome.htm</Description>
<CopyLocation>0</CopyLocation>
<FileType>.htm</FileType>
</File>
<File>
<Name>..\shared\fonts\khmer\mondulkiri\FONTLOG.txt</Name>
<Description>File FONTLOG.txt</Description>
<CopyLocation>0</CopyLocation>
<FileType>.txt</FileType>
</File>
<File>
<Name>..\shared\fonts\khmer\mondulkiri\Mondulkiri-R.ttf</Name>
<Description>Font Khmer Mondulkiri</Description>
<CopyLocation>0</CopyLocation>
<FileType>.ttf</FileType>
</File>
<File>
<Name>..\shared\fonts\khmer\mondulkiri\OFL.txt</Name>
<Description>File OFL.txt</Description>
<CopyLocation>0</CopyLocation>
<FileType>.txt</FileType>
</File>
<File>
<Name>..\shared\fonts\khmer\mondulkiri\OFL-FAQ.txt</Name>
<Description>File OFL-FAQ.txt</Description>
<CopyLocation>0</CopyLocation>
<FileType>.txt</FileType>
</File>
<File>
<Name>welcome\KAK_Documentation_EN.pdf</Name>
<Description>File KAK_Documentation_EN.pdf</Description>
<CopyLocation>0</CopyLocation>
<FileType>.pdf</FileType>
</File>
<File>
<Name>welcome\KAK_Documentation_KH.pdf</Name>
<Description>File KAK_Documentation_KH.pdf</Description>
<CopyLocation>0</CopyLocation>
<FileType>.pdf</FileType>
</File>
<File>
<Name>readme.htm</Name>
<Description>File readme.htm</Description>
<CopyLocation>0</CopyLocation>
<FileType>.htm</FileType>
</File>
<File>
<Name>welcome\image002.png</Name>
<Description>File image002.png</Description>
<CopyLocation>0</CopyLocation>
<FileType>.png</FileType>
</File>
<File>
<Name>..\shared\fonts\khmer\busrakbd\khmer_busra_kbd.ttf</Name>
<Description>Font KhmerBusraKbd</Description>
<CopyLocation>0</CopyLocation>
<FileType>.ttf</FileType>
</File>
<File>
<Name>splash.gif</Name>
<Description>File splash.gif</Description>
<CopyLocation>0</CopyLocation>
<FileType>.gif</FileType>
<FileType>.kmx</FileType>
</File>
</Files>
<Keyboards>
<Keyboard>
<Name>Khmer Angkor</Name>
<ID>khmer_angkor</ID>
<ID>keyboardfilenotfound</ID>
<Version>1.3</Version>
<OSKFont>..\shared\fonts\khmer\busrakbd\khmer_busra_kbd.ttf</OSKFont>
<DisplayFont>..\shared\fonts\khmer\mondulkiri\Mondulkiri-R.ttf</DisplayFont>
<Languages>
<Language ID="km">Central Khmer (Khmer, Cambodia)</Language>
</Languages>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<Package>
<System>
<KeymanDeveloperVersion>15.0.266.0</KeymanDeveloperVersion>
<FileVersion>7.0</FileVersion>
</System>
<Info>
<Name URL="">SENĆOŦEN (Saanich Dialect) Lexical Model</Name>
<Copyright URL="">© 2019 National Research Council Canada</Copyright>
<Author URL="mailto:Eddie.Santos@nrc-cnrc.gc.ca">Eddie Antonio Santos</Author>
<Version>1.0.3</Version>
</Info>
<Files>
<File>
<Name>example.qaa.sencoten.model.js</Name>
<Description>Lexical model example.qaa.sencoten.model.js</Description>
<CopyLocation>0</CopyLocation>
<FileType>.model.js</FileType>
</File>
</Files>
<LexicalModels>
<LexicalModel>
<Name>SENĆOŦEN dictionary</Name>
<ID>example.qaa.sencoten</ID>
<!-- ERROR_LexicalModelMustHaveAtLeastOneLanguage -->
</LexicalModel>
</LexicalModels>
</Package>

Binary file not shown.

View file

@ -0,0 +1,6 @@
store(&name) 'version 4'
store(&keyboardversion) '4.0'
begin unicode > use(main)
group(main) using keys

Binary file not shown.

View file

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<Package>
<System>
<KeymanDeveloperVersion>15.0.266.0</KeymanDeveloperVersion>
<FileVersion>7.0</FileVersion>
</System>
<Info>
<Name URL="">SENĆOŦEN (Saanich Dialect) Lexical Model</Name>
<Copyright URL="">© 2019 National Research Council Canada</Copyright>
<Author URL="mailto:Eddie.Santos@nrc-cnrc.gc.ca">Eddie Antonio Santos</Author>
<Version>1.3</Version>
</Info>
<Files>
<File>
<Name>khmer_angkor.kmx</Name>
<Description>Keyboard Khmer Angkor</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kmx</FileType>
</File>
<!-- warn_doc_file_dangerous -->
<File>
<Name>khmer_angkor.docx</Name>
<Description>Documentation</Description>
<CopyLocation>0</CopyLocation>
<FileType>.docx</FileType>
</File>
</Files>
<Keyboards>
<Keyboard>
<Name>Khmer Angkor</Name>
<ID>khmer_angkor</ID>
<Version>1.3</Version>
<Languages>
<Language ID="KM">Khmer</Language>
</Languages>
</Keyboard>
</Keyboards>
</Package>

View file

@ -8,7 +8,7 @@
<Name URL="">SENĆOŦEN (Saanich Dialect) Lexical Model</Name>
<Copyright URL="">© 2019 National Research Council Canada</Copyright>
<Author URL="mailto:Eddie.Santos@nrc-cnrc.gc.ca">Eddie Antonio Santos</Author>
<Version>1.0.3</Version>
<Version>1.3</Version>
</Info>
<Files>
<File>
@ -22,7 +22,7 @@
<Keyboards>
<Keyboard>
<Name>Khmer Angkor</Name>
<ID>nokeyboardversion</ID>
<ID>my special keyboard</ID>
<Version>1.3</Version>
<Languages>
<Language ID="km">Central Khmer (Khmer, Cambodia)</Language>

View file

@ -8,12 +8,19 @@
<Name URL="">SENĆOŦEN (Saanich Dialect) Lexical Model</Name>
<Copyright URL="">© 2019 National Research Council Canada</Copyright>
<Author URL="mailto:Eddie.Santos@nrc-cnrc.gc.ca">Eddie Antonio Santos</Author>
<Version>1.0.3</Version>
<Version>1.3</Version>
</Info>
<Files>
<File>
<!-- extension should not have capitals -->
<Name>my_special-keyboard.KMX</Name>
<Name>my_file.PDF</Name>
<Description>My Documentation</Description>
<CopyLocation>0</CopyLocation>
<FileType>.PDF</FileType>
</File>
<File>
<!-- extension should not have capitals -->
<Name>khmer_angkor.kmx</Name>
<Description>Keyboard my special keyboard</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kmx</FileType>
@ -22,7 +29,7 @@
<Keyboards>
<Keyboard>
<Name>Khmer Angkor</Name>
<ID>nokeyboardversion</ID>
<ID>khmer_angkor</ID>
<Version>1.3</Version>
<Languages>
<Language ID="km">Central Khmer (Khmer, Cambodia)</Language>

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<Package>
<System>
<KeymanDeveloperVersion>15.0.266.0</KeymanDeveloperVersion>
<FileVersion>7.0</FileVersion>
</System>
<Info>
<Name URL="">SENĆOŦEN (Saanich Dialect) Lexical Model</Name>
<Copyright URL="">© 2019 National Research Council Canada</Copyright>
<Author URL="mailto:Eddie.Santos@nrc-cnrc.gc.ca">Eddie Antonio Santos</Author>
<Version>1.3</Version>
</Info>
<Files>
<File>
<Name>khmer_angkor.kmx</Name>
<Description>Keyboard Khmer Angkor</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kmx</FileType>
</File>
<File>
<Name>version_four.kmx</Name>
<Description>Keyboard Version Four</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kmx</FileType>
</File>
</Files>
<Keyboards>
<Keyboard>
<Name>Khmer Angkor</Name>
<ID>khmer_angkor</ID>
<Version>1.3</Version>
<Languages>
<Language ID="km">Khmer</Language>
</Languages>
</Keyboard>
<Keyboard>
<Name>Version 4</Name>
<ID>version_four</ID>
<Version>4.0</Version>
<Languages>
<Language ID="km">Khmer</Language>
</Languages>
</Keyboard>
</Keyboards>
</Package>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<Package>
<System>
<KeymanDeveloperVersion>15.0.266.0</KeymanDeveloperVersion>
<FileVersion>7.0</FileVersion>
</System>
<Info>
<Name URL="">SENĆOŦEN (Saanich Dialect) Lexical Model</Name>
<Copyright URL="">© 2019 National Research Council Canada</Copyright>
<Author URL="mailto:Eddie.Santos@nrc-cnrc.gc.ca">Eddie Antonio Santos</Author>
<Version>1.3</Version>
</Info>
<Files>
<File>
<Name>version_four.kmx</Name>
<Description>Keyboard Version Four</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kmx</FileType>
</File>
</Files>
<Keyboards>
<Keyboard>
<Name>Version 4</Name>
<ID>version_four</ID>
<Version>4.0</Version>
<Languages>
<Language ID="km">Khmer</Language>
</Languages>
</Keyboard>
</Keyboards>
</Package>

View file

@ -4,10 +4,15 @@
<KeymanDeveloperVersion>15.0.266.0</KeymanDeveloperVersion>
<FileVersion>7.0</FileVersion>
</System>
<!-- Missing <Info>/<Name/> -->
<Info>
<Name URL="">SENĆOŦEN (Saanich Dialect) Lexical Model</Name>
<Copyright URL="">© 2019 National Research Council Canada</Copyright>
<Author URL="mailto:Eddie.Santos@nrc-cnrc.gc.ca">Eddie Antonio Santos</Author>
<Version>1.3</Version>
</Info>
<Files>
<File>
<Name>nokeyboardversion.kmx</Name>
<Name>khmer_angkor.kmx</Name>
<Description>Keyboard Khmer Angkor</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kmx</FileType>
@ -16,10 +21,11 @@
<Keyboards>
<Keyboard>
<Name>Khmer Angkor</Name>
<ID>nokeyboardversion</ID>
<ID>khmer_angkor</ID>
<Version>1.3</Version>
<Languages>
<Language ID="km">Central Khmer (Khmer, Cambodia)</Language>
<!-- WARN_LanguageTagIsNotMinimal -->
<Language ID="km-Khmr-KH">Central Khmer (Khmer, Cambodia)</Language>
</Languages>
</Keyboard>
</Keyboards>

View file

@ -8,11 +8,11 @@
<Name URL="">SENĆOŦEN (Saanich Dialect) Lexical Model</Name>
<Copyright URL="">© 2019 National Research Council Canada</Copyright>
<Author URL="mailto:Eddie.Santos@nrc-cnrc.gc.ca">Eddie Antonio Santos</Author>
<Version>1.0.3</Version>
<Version>1.3</Version>
</Info>
<Files>
<File>
<Name>nokeyboardversion.kmx</Name>
<Name>khmer_angkor.kmx</Name>
<Description>Keyboard Khmer Angkor</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kmx</FileType>
@ -21,7 +21,7 @@
<Keyboards>
<Keyboard>
<Name>Khmer Angkor</Name>
<ID>nokeyboardversion</ID>
<ID>khmer_angkor</ID>
<Version>1.3</Version>
<Languages>
<!-- WARN_PackageShouldNotRepeatLanguages -->

View file

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<Package>
<System>
<KeymanDeveloperVersion>15.0.266.0</KeymanDeveloperVersion>
<FileVersion>7.0</FileVersion>
</System>
<Info>
<Name URL="">SENĆOŦEN (Saanich Dialect) Lexical Model</Name>
<Copyright URL="">© 2019 National Research Council Canada</Copyright>
<Author URL="mailto:Eddie.Santos@nrc-cnrc.gc.ca">Eddie Antonio Santos</Author>
<Version>1.3</Version>
</Info>
<Files>
<File>
<Name>khmer_angkor.kmx</Name>
<Description>Keyboard Khmer Angkor</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kmx</FileType>
</File>
<!-- warn_redist_file_should_not_be_in_package -->
<File>
<Name>keyman.exe</Name>
<Description>Keyman Program</Description>
<CopyLocation>0</CopyLocation>
<FileType>.exe</FileType>
</File>
</Files>
<Keyboards>
<Keyboard>
<Name>Khmer Angkor</Name>
<ID>khmer_angkor</ID>
<Version>1.3</Version>
<Languages>
<Language ID="KM">Khmer</Language>
</Languages>
</Keyboard>
</Keyboards>
</Package>

View file

@ -17,6 +17,7 @@
</StartMenu>
<Info>
<Name URL="">XML KVK File</Name>
<Version>1.3</Version>
</Info>
<Files>
<File>
@ -25,5 +26,21 @@
<CopyLocation>0</CopyLocation>
<FileType>.kvk</FileType>
</File>
<File>
<Name>../../invalid/khmer_angkor.kmx</Name>
<Description>Keyboard Khmer Angkor</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kmx</FileType>
</File>
</Files>
<Keyboards>
<Keyboard>
<Name>Khmer Angkor</Name>
<ID>khmer_angkor</ID>
<Version>1.3</Version>
<Languages>
<Language ID="KM">Khmer</Language>
</Languages>
</Keyboard>
</Keyboards>
</Package>

View file

@ -13,6 +13,8 @@ import { KmpCompiler } from '../src/compiler/kmp-compiler.js';
import { PackageValidation } from '../src/compiler/package-validation.js';
import { CompilerMessages } from '../src/compiler/messages.js';
const debug = true;
describe('KmpCompiler', function () {
const MODELS : string[] = [
'example.qaa.sencoten',
@ -141,6 +143,8 @@ describe('KmpCompiler', function () {
await assert.isNull(kmpCompiler.buildKmpFile(kpsPath, kmpJson));
if(debug) callbacks.printMessages();
assert.lengthOf(callbacks.messages, 2);
assert.deepEqual(callbacks.messages[0].code, CompilerMessages.WARN_AbsolutePath);
assert.deepEqual(callbacks.messages[1].code, CompilerMessages.ERROR_FileDoesNotExist);
@ -169,7 +173,7 @@ describe('KmpCompiler', function () {
kmpCompiler.buildKmpFile(kpsPath, kmpJson)
}
//TODO: callbacks.printMessages(); after #8711 is merged
if(debug) callbacks.printMessages();
if(messageId) {
assert.lengthOf(callbacks.messages, 1);
@ -195,16 +199,16 @@ describe('KmpCompiler', function () {
testForMessage(this, ['invalid', 'followkeyboardversion.qaa.sencoten.model.kps'], CompilerMessages.ERROR_FollowKeyboardVersionNotAllowedForModelPackages);
});
// WARN_FollowKeyboardVersionButNoKeyboards
// ERROR_FollowKeyboardVersionButNoKeyboards
it('should generate WARN_FollowKeyboardVersionButNoKeyboards if <FollowKeyboardVersion> is set for a package with no keyboards or models', async function() {
testForMessage(this, ['invalid', 'followkeyboardversion.empty.kps'], CompilerMessages.WARN_FollowKeyboardVersionButNoKeyboards);
it('should generate ERROR_FollowKeyboardVersionButNoKeyboards if <FollowKeyboardVersion> is set for a package with no keyboards', async function() {
testForMessage(this, ['invalid', 'followkeyboardversion.empty.kps'], CompilerMessages.ERROR_FollowKeyboardVersionButNoKeyboards);
});
// ERROR_KeyboardFileNotFound
// ERROR_KeyboardContentFileNotFound
it('should generate ERROR_KeyboardFileNotFound if a <Keyboard> is listed in a package but not found in <Files>', async function() {
testForMessage(this, ['invalid', 'keyboardfilenotfound.kps'], CompilerMessages.ERROR_KeyboardFileNotFound);
it('should generate ERROR_KeyboardContentFileNotFound if a <Keyboard> is listed in a package but not found in <Files>', async function() {
testForMessage(this, ['invalid', 'keyboardcontentfilenotfound.kps'], CompilerMessages.ERROR_KeyboardContentFileNotFound);
});
// ERROR_KeyboardFileNotValid
@ -213,16 +217,16 @@ describe('KmpCompiler', function () {
testForMessage(this, ['invalid', 'keyboardfilenotvalid.kps'], CompilerMessages.ERROR_KeyboardFileNotValid);
});
// WARN_KeyboardFileHasNoKeyboardVersion
// INFO_KeyboardFileHasNoKeyboardVersion
it('should generate WARN_KeyboardFileHasNoKeyboardVersion if <FollowKeyboardVersion> is set but keyboard has no version', async function() {
testForMessage(this, ['invalid', 'nokeyboardversion.kps'], CompilerMessages.WARN_KeyboardFileHasNoKeyboardVersion);
it('should generate INFO_KeyboardFileHasNoKeyboardVersion if <FollowKeyboardVersion> is set but keyboard has no version', async function() {
testForMessage(this, ['invalid', 'nokeyboardversion.kps'], CompilerMessages.INFO_KeyboardFileHasNoKeyboardVersion);
});
// ERROR_PackageCannotContainBothModelsAndKeyboards
it('should generate ERROR_PackageCannotContainBothModelsAndKeyboards if package has both keyboards and models', async function() {
testForMessage(this, ['invalid', 'ERROR_PackageCannotContainBothModelsAndKeyboards.kps'], CompilerMessages.ERROR_PackageCannotContainBothModelsAndKeyboards);
testForMessage(this, ['invalid', 'error_package_cannot_contain_both_models_and_keyboards.kps'], CompilerMessages.ERROR_PackageCannotContainBothModelsAndKeyboards);
});
// WARN_PackageShouldNotRepeatLanguages (models)
@ -261,7 +265,63 @@ describe('KmpCompiler', function () {
it('should generate ERROR_PackageNameCannotBeBlank if package info has empty name', async function() {
testForMessage(this, ['invalid', 'error_package_name_cannot_be_blank.kps'], CompilerMessages.ERROR_PackageNameCannotBeBlank); // blank field
testForMessage(this, ['invalid', 'error_package_name_cannot_be_blank_2.kps'], CompilerMessages.ERROR_PackageNameCannotBeBlank); // missing field
testForMessage(this, ['invalid', 'error_package_name_cannot_be_blank_3.kps'], CompilerMessages.ERROR_PackageNameCannotBeBlank); // missing info section
});
// ERROR_KeyboardFileNotFound
it('should generate ERROR_KeyboardFileNotFound if a <Keyboard> is listed in a package but not found in <Files>', async function() {
testForMessage(this, ['invalid', 'keyboardfilenotfound.kps'], CompilerMessages.ERROR_KeyboardFileNotFound);
});
// WARN_KeyboardVersionsDoNotMatch
it('should generate WARN_KeyboardVersionsDoNotMatch if two <Keyboards> have different versions', async function() {
testForMessage(this, ['invalid', 'warn_keyboard_versions_do_not_match.kps'], CompilerMessages.WARN_KeyboardVersionsDoNotMatch);
});
// WARN_KeyboardVersionsDoNotMatchPackageVersion
it('should generate WARN_KeyboardVersionsDoNotMatchPackageVersion if <Keyboard> version does not match package version', async function() {
testForMessage(this, ['invalid', 'warn_keyboard_versions_do_not_match_package_version.kps'], CompilerMessages.WARN_KeyboardVersionsDoNotMatchPackageVersion);
});
// ERROR_LanguageTagIsNotValid
it('should generate ERROR_LanguageTagIsNotValid if keyboard has an invalid language tag', async function() {
testForMessage(this, ['invalid', 'error_language_tag_is_not_valid.kps'], CompilerMessages.ERROR_LanguageTagIsNotValid);
});
// WARN_LanguageTagIsNotMinimal
it('should generate WARN_LanguageTagIsNotMinimal if keyboard has a non-minimal language tag', async function() {
testForMessage(this, ['invalid', 'warn_language_tag_is_not_minimal.kps'], CompilerMessages.WARN_LanguageTagIsNotMinimal);
});
// ERROR_MustHaveAtLeastOneLanguage
it('should generate ERROR_MustHaveAtLeastOneLanguage if model or keyboard has zero language tags', async function() {
testForMessage(this, ['invalid', 'keyman.en.error_must_have_at_least_one_language.model.kps'],
CompilerMessages.ERROR_MustHaveAtLeastOneLanguage);
});
// WARN_RedistFileShouldNotBeInPackage
it('should generate WARN_RedistFileShouldNotBeInPackage if package contains a redist file', async function() {
testForMessage(this, ['invalid', 'warn_redist_file_should_not_be_in_package.kps'],
CompilerMessages.WARN_RedistFileShouldNotBeInPackage);
});
// WARN_DocFileDangerous
it('should generate WARN_DocFileDangerous if package contains a .doc file', async function() {
testForMessage(this, ['invalid', 'warn_doc_file_dangerous.kps'],
CompilerMessages.WARN_DocFileDangerous);
});
// ERROR_PackageMustContainAPackageOrAKeyboard
it('should generate ERROR_PackageMustContainAModelOrAKeyboard if package contains a .doc file', async function() {
testForMessage(this, ['invalid', 'error_package_must_contain_a_model_or_a_keyboard.kps'],
CompilerMessages.ERROR_PackageMustContainAModelOrAKeyboard);
});
});

View file

@ -1,6 +1,6 @@
import * as path from 'path';
import { BuildActivity, BuildActivityOptions } from './BuildActivity.js';
import { Compiler } from '@keymanapp/kmc-kmn';
import { KmnCompiler } from '@keymanapp/kmc-kmn';
import { platform } from 'os';
import { CompilerCallbacks } from '@keymanapp/common-types';
@ -10,8 +10,8 @@ export class BuildKmnKeyboard extends BuildActivity {
public get compiledExtension(): string { return '.kmx'; }
public get description(): string { return 'Build a Keyman keyboard'; }
public async build(infile: string, callbacks: CompilerCallbacks, options: BuildActivityOptions): Promise<boolean> {
let compiler = new Compiler();
if(!await compiler.init()) {
let compiler = new KmnCompiler();
if(!await compiler.init(callbacks)) {
return false;
}
@ -23,7 +23,7 @@ export class BuildKmnKeyboard extends BuildActivity {
// TODO: Currently this only builds .kmn->.kmx, and targeting .js is as-yet unsupported
// TODO: Support additional options compilerWarningsAsErrors, warnDeprecatedCode
return compiler.run(infile, outfile, callbacks,
return compiler.run(infile, outfile,
{
saveDebug: options.debug,
shouldAddCompilerVersion: options.compilerVersion,
@ -49,4 +49,4 @@ function getPosixAbsolutePath(filename: string): string {
filename = filename.replace(/\\/g, '/');
}
return filename;
}
}

View file

@ -1,6 +1,6 @@
import * as path from 'path';
import * as fs from 'fs';
import * as kmc from '@keymanapp/kmc-ldml';
import * as kmcLdml from '@keymanapp/kmc-ldml';
import { KvkFileWriter, CompilerCallbacks } from '@keymanapp/common-types';
import { BuildActivity, BuildActivityOptions } from './BuildActivity.js';
@ -43,14 +43,14 @@ export class BuildLdmlKeyboard extends BuildActivity {
}
function buildLdmlKeyboardToMemory(inputFilename: string, callbacks: CompilerCallbacks, options: BuildActivityOptions): [Uint8Array, Uint8Array, Uint8Array] {
let compilerOptions: kmc.CompilerOptions = {
let compilerOptions: kmcLdml.CompilerOptions = {
debug: options.debug ?? false,
addCompilerVersion: options.compilerVersion ?? true,
// TODO: warnDeprecatedCode: options.warnDeprecatedCode,
// TODO: treatWarningsAsErrors: options.treatWarningsAsErrors,
}
const k = new kmc.Compiler(callbacks, options);
const k = new kmcLdml.LdmlKeyboardCompiler(callbacks, options);
let source = k.load(inputFilename);
if (!source) {
return [null, null, null];
@ -62,13 +62,13 @@ function buildLdmlKeyboardToMemory(inputFilename: string, callbacks: CompilerCal
// In order for the KMX file to be loaded by non-KMXPlus components, it is helpful
// to duplicate some of the metadata
kmc.KMXPlusMetadataCompiler.addKmxMetadata(kmx.kmxplus, kmx.keyboard, compilerOptions);
kmcLdml.KMXPlusMetadataCompiler.addKmxMetadata(kmx.kmxplus, kmx.keyboard, compilerOptions);
// Use the builder to generate the binary output file
const builder = new kmc.KMXBuilder(kmx, options.debug);
const builder = new kmcLdml.KMXBuilder(kmx, options.debug);
const kmx_binary = builder.compile();
const vkcompiler = new kmc.VisualKeyboardCompiler();
const vkcompiler = new kmcLdml.LdmlKeyboardVisualKeyboardCompiler();
const vk = vkcompiler.compile(source);
const writer = new KvkFileWriter();
const kvk_binary = writer.write(vk);
@ -78,7 +78,7 @@ function buildLdmlKeyboardToMemory(inputFilename: string, callbacks: CompilerCal
// const tlcompiler = new kmc.TouchLayoutCompiler();
// const tl = tlcompiler.compile(source);
// const tlwriter = new TouchLayoutFileWriter();
const kmwcompiler = new kmc.KeymanWebCompiler(callbacks, compilerOptions);
const kmwcompiler = new kmcLdml.LdmlKeyboardKeymanWebCompiler(callbacks, compilerOptions);
const kmw_string = kmwcompiler.compile(inputFilename, source);
const encoder = new TextEncoder();
const kmw_binary = encoder.encode(kmw_string);

View file

@ -29,7 +29,7 @@ export function buildTestData(infile: string, options: BuildTestDataOptions) {
function loadTestData(inputFilename: string, options: kmc.CompilerOptions): LDMLKeyboardTestDataXMLSourceFile {
const c: CompilerCallbacks = new NodeCompilerCallbacks();
const k = new kmc.Compiler(c, options);
const k = new kmc.LdmlKeyboardCompiler(c, options);
let source = k.loadTestData(inputFilename);
if (!source) {
return null;

View file

@ -48,6 +48,9 @@ let jsFilename = program.opts().jsFilename ? program.opts().jsFilename : path.jo
const callbacks = new NodeCompilerCallbacks();
let kmpCompiler = new KmpCompiler(callbacks);
let kmpJsonData = kmpCompiler.transformKpsToKmpObject(kpsFilename);
if(!kmpJsonData) {
process.exit(1);
}
//
// Validate the package file

View file

@ -18,7 +18,7 @@
<Name URL="">k_000___null_keyboard</Name>
<Copyright URL="">Copyright (C) Keyman Team</Copyright>
<Author URL="">Keyman Team</Author>
<Version URL=""></Version>
<Version URL="">0.0</Version>
</Info>
<Files>
<File>

View file

@ -16,8 +16,9 @@ describe('BuildProject', function () {
debug: false,
warnDeprecatedCode: true,
});
// 4 messages == starting build, build successful x 2
assert.equal(callbacks.messages.length, 4);
// 5 messages == starting build, info: no keyboard version, build successful x 2
// callbacks.printMessages();
assert.equal(callbacks.messages.length, 5);
assert.isTrue(result);
});
});

View file

@ -76,14 +76,10 @@ static const int KMCMP_ERROR_HAS_STRINGS = -2;
* Error: Invalid, uses properties \p{Mn} or [:Mn:]
*/
static const int KMCMP_ERROR_UNSUPPORTED_PROPERTY = -3;
/**
* Error: Invalid, other unsupported feature
*/
static const int KMCMP_ERROR_UNSUPPORTED = -4;
/**
* Fatal: output buffer too small
*/
static const int KMCMP_FATAL_OUT_OF_RANGE = -5;
static const int KMCMP_FATAL_OUT_OF_RANGE = -4;
/**
* Function pointer to kmcmp_ParseUnicodeSet

View file

@ -65,6 +65,15 @@ EXTERN bool kmcmp_Wasm_CompileKeyboardFile(char* pszInfile,
msgProc
);
}
EXTERN int kmcmp_Wasm_ParseUnicodeSet(char* pat,
uint32_t* buf, int length
) {
return kmcmp_ParseUnicodeSet(
pat, buf, length
);
}
#endif
EXTERN bool kmcmp_CompileKeyboardFile(char* pszInfile,
@ -365,4 +374,4 @@ bool CompileKeyboardHandle(FILE* fp_in, PFILE_KEYBOARD fk)
kmcmp::CheckForDeprecatedFeatures(fk);
return TRUE;
}
}

View file

@ -1,108 +0,0 @@
unit keyman32_int;
interface
uses Windows, SysUtils, Forms;
function Keyman_Initialise(Handle: HWND; FSingleApp: Boolean): Boolean;
function Keyman_Exit: Boolean;
function Keyman_ForceKeyboard(const s: string): Boolean;
function Keyman_StopForcingKeyboard: Boolean;
implementation
uses TikeUtils;
type TKeyman_ForceKeyboard = function (s: PChar): Boolean; stdcall;
type TKeyman_StopForcingKeyboard = function: Boolean; stdcall;
type TKeyman_Initialise = function(h: THandle; FSingleApp: LongBool): Boolean; stdcall;
type TKeyman_Exit = function: Boolean; stdcall;
var
FInitKeyman: Boolean = False;
FKeyman32Path: string = '';
function Keyman_Initialise(Handle: HWND; FSingleApp: Boolean): Boolean;
var
hkeyman: THandle;
FLoad: Boolean;
ki: TKeyman_Initialise;
begin
Result := False;
FLoad := False;
hkeyman := GetModuleHandle('keyman32.dll');
if hkeyman = 0 then
begin
hkeyman := LoadLibrary(PChar(GetKeymanInstallPath+'keyman32.dll'));
if hkeyman = 0 then Exit;
FLoad := True;
end;
ki := TKeyman_Initialise(GetProcAddress(hkeyman, 'Keyman_Initialise'));
if not Assigned(@ki) then Exit;
if not ki(Handle, FSingleApp) then
begin
if FLoad then FreeLibrary(hkeyman);
Exit;
end;
FInitKeyman := True;
Result := True;
end;
function Keyman_Exit: Boolean;
var
hkeyman: THandle;
ke: TKeyman_Exit;
begin
if not FInitKeyman then
begin
Result := True;
Exit;
end;
Result := False;
hkeyman := GetModuleHandle('keyman32.dll');
if hkeyman = 0 then Exit;
ke := TKeyman_Exit(GetProcAddress(hkeyman, 'Keyman_Exit'));
if not Assigned(@ke) then Exit;
if not ke then Exit;
if FInitKeyman then FreeLibrary(hkeyman);
FInitKeyman := False;
Result := True;
end;
function Keyman_ForceKeyboard(const s: string): Boolean;
var
hkeyman: THandle;
fk: TKeyman_ForceKeyboard;
begin
Result := False;
hkeyman := GetModuleHandle('keyman32.dll');
if hkeyman = 0 then
begin
if not Keyman_Initialise(Application.MainForm.Handle, True) then Exit;
hkeyman := GetModuleHandle('keyman32.dll');
if hkeyman = 0 then Exit;
end;
fk := TKeyman_ForceKeyboard(GetProcAddress(hkeyman, 'Keyman_ForceKeyboard'));
if(Assigned(@fk)) then
Result := fk(PChar(s));
end;
function Keyman_StopForcingKeyboard: Boolean;
var
hkeyman: THandle;
sfk: TKeyman_StopForcingKeyboard;
begin
Result := False;
hkeyman := GetModuleHandle('keyman32.dll');
if hkeyman = 0 then Exit;
sfk := TKeyman_StopForcingKeyboard(GetProcAddress(hkeyman, 'Keyman_StopForcingKeyboard'));
if(Assigned(@sfk)) then
Result := sfk;
Keyman_Exit;
end;
initialization
finalization
Keyman_Exit;
end.

View file

@ -1,5 +1,5 @@
github "marmelroy/Zip"
github "DaveWoodCom/XCGLogger" ~> 6.1.0
github "keymanapp/dependency-XCGLogger" "master"
github "devicekit/DeviceKit" ~> 5.0
github "ashleymills/Reachability.swift"
github "getsentry/sentry-cocoa" ~> 6.2.1
github "getsentry/sentry-cocoa" ~> 8.7.0

View file

@ -1,5 +1,5 @@
github "DaveWoodCom/XCGLogger" "6.1.0"
github "ashleymills/Reachability.swift" "v5.1.0"
github "devicekit/DeviceKit" "5.0.0"
github "getsentry/sentry-cocoa" "6.2.1"
github "getsentry/sentry-cocoa" "8.7.0"
github "keymanapp/dependency-XCGLogger" "57a7b975dbb6fe4fe90cef3d1bc52b8adbd89113"
github "marmelroy/Zip" "2.1.2"

View file

@ -167,7 +167,7 @@ public class SentryManager {
public static func breadcrumbAndLog(crumb: Sentry.Breadcrumb, logLevel: XCGLogger.Level? = nil) {
// Guarded in case a library consumer decides against initializing Sentry.
if _started {
SentrySDK.addBreadcrumb(crumb: crumb)
SentrySDK.addBreadcrumb(crumb)
}
let level = logLevel ?? mapLoggingLevel(crumb.level)
@ -193,7 +193,7 @@ public class SentryManager {
}
public static func forceError() {
SentrySDK.addBreadcrumb(crumb: Sentry.Breadcrumb(level: .info, category: "Deliberate testing error"))
SentrySDK.addBreadcrumb(Sentry.Breadcrumb(level: .info, category: "Deliberate testing error"))
SentrySDK.crash()
}
}

View file

@ -1,7 +1,7 @@
#!/bin/sh
#!/bin/bash
# Don't call `set -e`. Even if some commands should fail, it's still
# worth running the rest of the commands.
# Exit on errors - Debian policy 10.4
set -e
case "$1" in
@ -12,7 +12,7 @@ case "$1" in
if which sudo > /dev/null && which ps > /dev/null; then
# check for gnome-shell as it works differently
gspid=$(ps -C gnome-shell -o pid=|head -n 1)
! gspid=$(ps -C gnome-shell -o pid=|head -n 1)
if [ "$gspid" != "" ]; then
# gnome-shell has multiple ibus-daemon processes and needs exit instead of restart
is_gnome_shell=1
@ -21,7 +21,7 @@ case "$1" in
fi
# Restart IBus if it is running
ibuspid=$(ps -C ibus-daemon -o pid=|head -n 1)
! ibuspid=$(ps -C ibus-daemon -o pid=|head -n 1)
if [ "$ibuspid" != "" ]; then
if [ "$is_gnome_shell" = "1" ]; then
@ -41,7 +41,7 @@ case "$1" in
# Verify that it's running now
if [ -n "$SUDO_USER" ] && id "$SUDO_USER" > /dev/null 2>/dev/null; then
ibusdaemon=$(ps --user "$SUDO_USER" -o s= -o cmd | grep --regexp="^[^ZT] \(/usr/bin/\)\?ibus-daemon .*--xim.*")
! ibusdaemon=$(ps --user "$SUDO_USER" -o s= -o cmd | grep --regexp="^[^ZT] \(/usr/bin/\)\?ibus-daemon .*--xim.*")
if [ "$ibusdaemon" = "" ]; then
# otherwise try to start it for the user installing the package
if [ "$is_gnome_shell" = "1" ]; then

View file

@ -6,7 +6,14 @@ default: clean version man langtags
langtags:
cd buildtools && python3 ./build-langtags.py
install: # run as sudo
install:
if [ -n "${SUDO_USER}" ]; then \
make install-sudo; \
else \
make install-temp; \
fi
install-sudo: # run as sudo
pip3 install qrcode sentry-sdk
# eventually change this to: pip3 install .
python3 setup.py install

View file

@ -1,4 +1,4 @@
github "DaveWoodCom/XCGLogger" ~> 6.1.0
github "keymanapp/dependency-XCGLogger" "master"
github "devicekit/DeviceKit" ~> 5.0
github "ashleymills/Reachability.swift"
github "getsentry/sentry-cocoa" ~> 6.2.1
github "getsentry/sentry-cocoa" ~> 8.7.0

View file

@ -1,4 +1,4 @@
github "DaveWoodCom/XCGLogger" "6.1.0"
github "ashleymills/Reachability.swift" "v5.1.0"
github "devicekit/DeviceKit" "5.0.0"
github "getsentry/sentry-cocoa" "6.2.1"
github "getsentry/sentry-cocoa" "8.7.0"
github "keymanapp/dependency-XCGLogger" "57a7b975dbb6fe4fe90cef3d1bc52b8adbd89113"

View file

@ -359,7 +359,7 @@ _builder_failure_trap() {
# finishes.
#
_builder_cleanup_deps() {
if ! builder_is_dep_build && [[ ! -z ${_builder_deps_built+x} ]]; then
if ! builder_is_dep_build && ! builder_is_child_build && [[ ! -z ${_builder_deps_built+x} ]]; then
if $_builder_debug_internal; then
builder_echo_debug "Dependencies that were built:"
cat "$_builder_deps_built"
@ -558,7 +558,6 @@ builder_has_action() {
function builder_run_action() {
local action=$1
shift
echo "builder_run_action $action $@"
if builder_start_action $action; then
($@)
builder_finish_action success $action
@ -1758,6 +1757,7 @@ builder_has_dependencies() {
builder_has_module_been_built() {
local module="$1"
if [[ -z ${_builder_deps_built+x} ]]; then
# not in a builder context, so we assume a build is needed
return 1

View file

@ -45,12 +45,6 @@ BOOL GetKeyboardFileName(LPSTR kbname, LPSTR buf, int nbuf)
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
if(_td->ForceFileName[0])
{
strncpy_s(buf, nbuf, _td->ForceFileName, nbuf - 1);
buf[nbuf-1] = 0;
return TRUE;
}
int n = 0;
RegistryReadOnly *reg = Reg_GetKeymanInstalledKeyboard(kbname);
@ -75,9 +69,9 @@ BOOL GetKeyboardFileName(LPSTR kbname, LPSTR buf, int nbuf)
return n;
}
BOOL LoadlpKeyboardCore(int i)
BOOL LoadlpKeyboard(int i)
{
SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboardCore: Enter ---");
SendDebugMessageFormat(0, sdmLoad, 0, "%s: Enter ---", __FUNCTION__);
PKEYMAN64THREADDATA _td = ThreadGlobals();
if (!_td) return FALSE;
@ -85,7 +79,7 @@ BOOL LoadlpKeyboardCore(int i)
if (_td->lpActiveKeyboard == &_td->lpKeyboards[i]) _td->lpActiveKeyboard = NULL; // I822 TSF not working
if (_td->lpKeyboards[i].lpCoreKeyboardState) {
SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboardCore: a keyboard km_kbp_state exits without matching keyboard - disposing of state");
SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboard: a keyboard km_kbp_state exits without matching keyboard - disposing of state");
km_kbp_state_dispose(_td->lpKeyboards[i].lpCoreKeyboardState);
_td->lpKeyboards[i].lpCoreKeyboardState = NULL;
}
@ -95,7 +89,7 @@ BOOL LoadlpKeyboardCore(int i)
PWCHAR keyboardPath = strtowstr(buf);
km_kbp_status err_status = km_kbp_keyboard_load(keyboardPath, &_td->lpKeyboards[i].lpCoreKeyboard);
if (err_status != KM_KBP_STATUS_OK) {
SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboardCore: km_kbp_keyboard_load failed for %ls with error status [%d]", keyboardPath, err_status);
SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboard: km_kbp_keyboard_load failed for %ls with error status [%d]", keyboardPath, err_status);
delete keyboardPath;
return FALSE;
}
@ -104,7 +98,7 @@ BOOL LoadlpKeyboardCore(int i)
km_kbp_option_item *core_environment = nullptr;
if(!SetupCoreEnvironment(&core_environment)) {
SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboardCore: Unable to set environment options for keyboard %ls", keyboardPath);
SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboard: Unable to set environment options for keyboard %ls", keyboardPath);
return FALSE;
}
@ -114,7 +108,7 @@ BOOL LoadlpKeyboardCore(int i)
if (err_status != KM_KBP_STATUS_OK) {
SendDebugMessageFormat(
0, sdmLoad, 0, "LoadlpKeyboardCore: km_kbp_state_create failed with error status [%d]", err_status);
0, sdmLoad, 0, "LoadlpKeyboard: km_kbp_state_create failed with error status [%d]", err_status);
// Dispose of the keyboard to leave us in a consistent state
ReleaseKeyboardMemoryCore(&_td->lpKeyboards[i].lpCoreKeyboard);
return FALSE;
@ -122,38 +116,15 @@ BOOL LoadlpKeyboardCore(int i)
// Register callback?
err_status = km_kbp_keyboard_get_imx_list(_td->lpKeyboards[i].lpCoreKeyboard, &_td->lpKeyboards[i].lpIMXList);
if (err_status != KM_KBP_STATUS_OK) {
SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboardCore: km_kbp_keyboard_get_imx_list failed with error status [%d]", err_status);
SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboard: km_kbp_keyboard_get_imx_list failed with error status [%d]", err_status);
// Dispose of the keyboard to leave us in a consistent state
ReleaseKeyboardMemoryCore(&_td->lpKeyboards[i].lpCoreKeyboard);
return FALSE;
}
LoadDLLsCore(&_td->lpKeyboards[i]);
LoadKeyboardOptionsREGCore(&_td->lpKeyboards[i], _td->lpKeyboards[i].lpCoreKeyboardState);
return TRUE;
}
BOOL LoadlpKeyboard(int i)
{
if (Globals::get_CoreIntegration())
{
return LoadlpKeyboardCore(i);
}
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
if(_td->lpKeyboards[i].Keyboard) return TRUE;
if(_td->lpActiveKeyboard == &_td->lpKeyboards[i]) _td->lpActiveKeyboard = NULL; // I822 TSF not working
char buf[256];
if(!GetKeyboardFileName(_td->lpKeyboards[i].Name, buf, 255)) return FALSE;
if(!LoadKeyboard(buf, &_td->lpKeyboards[i].Keyboard)) return FALSE; // I5136
LoadDLLs(&_td->lpKeyboards[i]);
LoadKeyboardOptions(&_td->lpKeyboards[i]);
LoadKeyboardOptionsRegistrytoCore(&_td->lpKeyboards[i], _td->lpKeyboards[i].lpCoreKeyboardState);
return TRUE;
}

View file

@ -1,263 +0,0 @@
/*
Name: addins
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Create Date: 14 Jun 2008
Modified Date: 14 May 2010
Authors: mcdurdin
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
History: 14 Jun 2008 - mcdurdin - I1488 - Fix registry handle leak
11 Mar 2009 - mcdurdin - I1894 - Fix threading bugs introduced in I1888
11 Dec 2009 - mcdurdin - I934 - x64 - Initial version
12 Mar 2010 - mcdurdin - I934 - x64 - Complete
12 Mar 2010 - mcdurdin - I2229 - Remove hints and warnings
04 May 2010 - mcdurdin - I2351 - Robustness - verify _td return value
14 May 2010 - mcdurdin - I2374 - Fix crash in some situations
*/
#include "pch.h"
void Addin_Release()
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return;
//SendDebugMessageFormat(GetFocus(), sdmGlobal, 0, "Addin_Release: ENTER [%d]", nAddins);
if(_td->Addins)
{
for(int i = 0; i < _td->nAddins; i++)
if(_td->Addins[i].hAddin)
{
if(_td->Addins[i].Uninitialise) (*_td->Addins[i].Uninitialise)();
FreeLibrary(_td->Addins[i].hAddin);
}
delete[] _td->Addins;
}
_td->Addins = NULL;
_td->nAddins = 0;
_td->CurrentAddin = -1;
//SendDebugMessageFormat(GetFocus(), sdmGlobal, 0, "Addin_Release: EXIT");
}
void ReadAddins(HKEY hkey)
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return;
//SendDebugMessageFormat(GetFocus(), sdmGlobal, 0, "Addins: ReadAddins: ENTER");
RegistryReadOnly *reg = new RegistryReadOnly(hkey);
if(reg->OpenKeyReadOnly(hkey == HKEY_CURRENT_USER ? REGSZ_KeymanAddinsCU : REGSZ_KeymanAddinsLM))
{
int n = _td->nAddins;
char buf[128];
while(reg->GetValueNames(buf, 128, n))
{
Addin *a = new Addin[n+1];
if(_td->Addins)
{
memcpy(a, _td->Addins, n * sizeof(Addin));
delete[] _td->Addins;
}
_td->Addins = a;
_td->Addins[n].hAddin = 0;
_td->Addins[n].FocusChanged = NULL;
_td->Addins[n].Initialise = NULL;
_td->Addins[n].OutputBackspace = NULL;
_td->Addins[n].OutputChar = NULL;
_td->Addins[n].Uninitialise = NULL;
_td->Addins[n].ShouldProcess = NULL;
strcpy(_td->Addins[n].ClassName, buf);
reg->ReadString(buf, _td->Addins[n].AddinName, 260);
_td->Addins[n].Application[0] = 0;
n++;
}
_td->nAddins = n;
}
delete reg;
//SendDebugMessageFormat(GetFocus(), sdmGlobal, 0, "Addins: ReadAddins: EXIT");
}
void Addin_Refresh()
{
//SendDebugMessageFormat(GetFocus(), sdmGlobal, 0, "Addin_Refresh: ENTER");
Addin_Release();
ReadAddins(HKEY_CURRENT_USER);
ReadAddins(HKEY_LOCAL_MACHINE);
//SendDebugMessageFormat(GetFocus(), sdmGlobal, 0, "Addin_Refresh: EXIT");
}
BOOL LoadAddin()
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
if(_td->CurrentAddin == -1) return FALSE;
Addin *a = &_td->Addins[_td->CurrentAddin];
if(!a->hAddin)
{
//SendDebugMessageFormat(GetFocus(), sdmGlobal, 0, "Addins: LoadAddin: ENTER");
a->hAddin = LoadLibrary(a->AddinName);
if(!a->hAddin)
{
a->hAddin = 0;
a->ClassName[0] = 0; // prevent add-in attempting to load again
_td->CurrenthWnd = 0;
_td->CurrentAddin = -1;
//SendDebugMessageFormat(GetFocus(), sdmGlobal, 0, "Addins: LoadAddin: EXIT - FALSE - LoadLibrary");
return FALSE;
}
a->OutputBackspace = (PKeymanOutputBackspace) GetProcAddress(a->hAddin, "KeymanOutputBackspace");
a->OutputChar = (PKeymanOutputChar) GetProcAddress(a->hAddin, "KeymanOutputChar");
a->FocusChanged = (PKeymanFocusChanged) GetProcAddress(a->hAddin, "KeymanFocusChanged");
a->ShouldProcess = (PKeymanShouldProcess) GetProcAddress(a->hAddin, "KeymanShouldProcess");
a->Initialise = (PKeymanInit) GetProcAddress(a->hAddin, "KeymanInitialise");
a->Uninitialise = (PKeymanInit) GetProcAddress(a->hAddin, "KeymanUninitialise");
if(a->Initialise && !(*a->Initialise)())
{
FreeLibrary(a->hAddin);
a->hAddin = 0;
a->ClassName[0] = 0;
_td->CurrenthWnd = 0;
_td->CurrentAddin = -1;
//SendDebugMessageFormat(GetFocus(), sdmGlobal, 0, "Addins: LoadAddin: EXIT - FALSE - Initialise");
return FALSE;
}
//SendDebugMessageFormat(GetFocus(), sdmGlobal, 0, "Addins: LoadAddin: EXIT - TRUE - Loaded");
}
return TRUE;
}
BOOL Addin_ShouldProcessUnichar(HWND hwnd)
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
//SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ShouldProcessUnichar: ENTER");
_td->CurrenthWnd = hwnd;
GetClassName(hwnd, _td->CurrentClassName, 128);
if(_td->CurrentAddin >= 0 && !_strcmpi(_td->CurrentClassName, _td->Addins[_td->CurrentAddin].ClassName))
{
//SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ShouldProcessUnichar: EXIT - TRUE - CurrentAddin okay");
return TRUE;
}
for(int i = 0; i < _td->nAddins; i++)
if(!_strcmpi(_td->CurrentClassName, _td->Addins[i].ClassName))
{
_td->CurrentAddin = i;
if(!LoadAddin())
{
_td->CurrentAddin = -1;
_td->Addins[i].ClassName[0] = 0; // prevent add-in attempting to load again
}
else if(_td->Addins[i].ShouldProcess && !(*_td->Addins[i].ShouldProcess)(hwnd))
_td->CurrentAddin = -1;
else
{
//SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ShouldProcessUnichar: EXIT - TRUE - FoundAddin");
return TRUE;
}
}
_td->CurrentAddin = -1;
_td->CurrenthWnd = 0;
//SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ShouldProcessUnichar: EXIT - FALSE");
return FALSE;
}
BOOL Addin_ProcessUnichar(HWND hwnd, DWORD chr)
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
//SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ProcessUnichar: ENTER");
if(_td->CurrenthWnd != hwnd)
if(!Addin_ShouldProcessUnichar(hwnd))
{
//SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ProcessUnichar: EXIT - FALSE - Addin_ShouldProcessUnichar");
return FALSE;
}
if(!LoadAddin())
{
//SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ProcessUnichar: EXIT - FALSE - !LoadAddin)");
return FALSE;
}
if(!_td->Addins[_td->CurrentAddin].OutputChar)
{
//SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ProcessUnichar: EXIT - FALSE !OutputChar");
return FALSE;
}
BOOL b = (*_td->Addins[_td->CurrentAddin].OutputChar)(hwnd, chr);
//SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ProcessUnichar: EXIT (b) == %d", b);
return b;
}
BOOL Addin_ProcessBackspace(HWND hwnd)
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
//SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ProcessBackspace: ENTER");
if(_td->CurrenthWnd != hwnd)
if(!Addin_ShouldProcessUnichar(hwnd))
{
//SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ProcessBackspace: EXIT - !ShouldProcess");
return FALSE;
}
if(!LoadAddin())
{
//SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ProcessBackspace: EXIT - !LoadAddin");
return FALSE;
}
if(!_td->Addins[_td->CurrentAddin].OutputBackspace)
{
//SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ProcessBackspace: EXIT - !OutputBackspace");
return FALSE;
}
BOOL b = (*_td->Addins[_td->CurrentAddin].OutputBackspace)(hwnd);
//SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ProcessBackspace: EXIT - (b) = %d", b);
return b;
}
void Addin_FocusChanged(HWND hwnd)
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return;
//SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_FocusChanged: ENTER");
if(_td->CurrenthWnd != hwnd)
if(!Addin_ShouldProcessUnichar(hwnd))
{
//SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_FocusChanged: EXIT - !ShouldProcess");
return;
}
if(!LoadAddin())
{
//SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_FocusChanged: EXIT - !LoadAddin");
return;
}
// Addin variables must be valid now
if(!_td->Addins[_td->CurrentAddin].FocusChanged)
{
//SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_FocusChanged: EXIT - !FocusChanged");
return;
}
(*_td->Addins[_td->CurrentAddin].FocusChanged)();
//SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_FocusChanged: EXIT");
}

View file

@ -1,23 +0,0 @@
/*
Name: addins
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Create Date: 11 Dec 2009
Modified Date: 11 Dec 2009
Authors: mcdurdin
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
History: 11 Dec 2009 - mcdurdin - I934 - x64 - Initial version
*/
void Addin_Release();
void Addin_Refresh();
BOOL Addin_ShouldProcessUnichar(HWND hwnd);
BOOL Addin_ProcessUnichar(HWND hwnd, DWORD chr);
BOOL Addin_ProcessBackspace(HWND hwnd);
void Addin_FocusChanged(HWND hwnd);

View file

@ -126,7 +126,6 @@ extern "C" __declspec(dllexport) BOOL WINAPI TIPProcessKey(WPARAM wParam, LPARAM
BOOL isUp = keyFlags & KF_UP ? TRUE : FALSE;
BOOL extended = keyFlags & KF_EXTENDED ? TRUE : FALSE;
BYTE scan = keyFlags & 0xFF;
BOOL isUsingCoreProcessor = Globals::get_CoreIntegration();
SendDebugMessageFormat(0, sdmAIDefault, 0, "TIPProcessKey: Enter VirtualKey=%s lParam=%x IsUp=%d Extended=%d Updateable=%d Preserved=%d", Debug_VirtualKey((WORD) wParam), lParam, isUp, extended, Updateable, Preserved);
@ -163,72 +162,31 @@ extern "C" __declspec(dllexport) BOOL WINAPI TIPProcessKey(WPARAM wParam, LPARAM
// core processor. The core processor has the keyboard Caps Lock stores and will
// queue an action 'KM_KBP_IT_CAPSLOCK'. In processing the action the Windows engine will synthesise keystrokes
// to ensure caps lock is in the correct state.
if (isUsingCoreProcessor) {
if (!Preserved) {
switch (wParam) {
case VK_MENU:
case VK_CONTROL:
ProcessModifierChange((UINT)wParam, isUp, extended);
return FALSE;
case VK_NUMLOCK:
if (!Preserved) {
switch (wParam) {
case VK_MENU:
case VK_CONTROL:
ProcessModifierChange((UINT)wParam, isUp, extended);
return FALSE;
case VK_NUMLOCK:
if (!isUp)
ProcessToggleChange((UINT)wParam); // I4793
return FALSE;
case VK_CAPITAL:
if (!isUp)
ProcessToggleChange((UINT)wParam); // I4793
return FALSE;
case VK_CAPITAL:
if (!isUp)
ProcessToggleChange((UINT)wParam); // I4793
break;
case VK_SHIFT:
ProcessModifierChange((UINT)wParam, isUp, extended);
ProcessToggleChange((UINT)wParam); // I4793
break;
}
} else {
// Mask out Ctrl, Shift and Alt and include new modifiers // I4548
DWORD NewShiftState = TSFShiftToShift(lParam); // I3588
SendDebugMessageFormat(
0, sdmGlobal, 0, "TIPProcessKey: TSFShiftToShift start with %x, include %x", LocalShiftState, NewShiftState);
*Globals::ShiftState() = (LocalShiftState & K_NOTMODIFIERFLAG) | NewShiftState; // I3588
case VK_SHIFT:
ProcessModifierChange((UINT)wParam, isUp, extended);
break;
}
} else { // using windows processor TODO: #5442 Remove this else block
if (!Preserved) {
switch (wParam) {
case VK_CAPITAL:
if (!isUp)
ProcessToggleChange((UINT)wParam); // I4793
if (!Updateable) {
// We only want to process the Caps Lock key event once --
// in the first pass (!Updateable).
KeyCapsLockPress(isUp); // I4548
}
return FALSE;
case VK_SHIFT:
if (!Updateable) {
// We only want to process the Shift key event once --
// in the first pass (!Updateable).
KeyShiftPress(isUp); // I4548
}
// Fall through
case VK_MENU:
case VK_CONTROL:
ProcessModifierChange((UINT)wParam, isUp, extended);
return FALSE;
case VK_NUMLOCK:
if (!isUp)
ProcessToggleChange((UINT)wParam); // I4793
return FALSE;
}
// This would only get here if none of the above cases matched why not use default in the switch?
if (isUp) {
return FALSE; // return value ignored in this case; we only needed it for testing anyway
}
} else {
// Mask out Ctrl, Shift and Alt and include new modifiers // I4548
DWORD NewShiftState = TSFShiftToShift(lParam); // I3588
SendDebugMessageFormat(
0, sdmGlobal, 0, "TIPProcessKey: TSFShiftToShift start with %x, include %x", LocalShiftState, NewShiftState);
*Globals::ShiftState() = (LocalShiftState & K_NOTMODIFIERFLAG) | NewShiftState; // I3588
}
} // TODO: #5442 Remove this else block ^^
} else {
// Mask out Ctrl, Shift and Alt and include new modifiers // I4548
DWORD NewShiftState = TSFShiftToShift(lParam); // I3588
SendDebugMessageFormat(
0, sdmGlobal, 0, "TIPProcessKey: TSFShiftToShift start with %x, include %x", LocalShiftState, NewShiftState);
*Globals::ShiftState() = (LocalShiftState & K_NOTMODIFIERFLAG) | NewShiftState; // I3588
}
_td->TIPFUpdateable = Updateable;
_td->TIPFPreserved = Preserved; // I4290
@ -239,16 +197,7 @@ extern "C" __declspec(dllexport) BOOL WINAPI TIPProcessKey(WPARAM wParam, LPARAM
_td->state.vkey = (WORD) wParam;
_td->state.isDown = !isUp;
if (isUsingCoreProcessor) {
_td->state.lpCoreKb = _td->lpActiveKeyboard->lpCoreKeyboard;
} else {
_td->state.lpkb = _td->lpActiveKeyboard->Keyboard;
_td->state.startgroup = &_td->state.lpkb->dpGroupArray[_td->state.lpkb->StartGroup[BEGIN_UNICODE]];
_td->state.NoMatches = TRUE;
_td->state.LoopTimes = 0;
_td->state.StopOutput = FALSE;
}
_td->state.lpCoreKb = _td->lpActiveKeyboard->lpCoreKeyboard;
_td->state.windowunicode = TRUE;
@ -257,30 +206,8 @@ extern "C" __declspec(dllexport) BOOL WINAPI TIPProcessKey(WPARAM wParam, LPARAM
_td->TIPProcessOutput = outfunc;
_td->TIPGetContext = ctfunc;
AppContextWithStores *savedContext = NULL; // I4370 // I4978
if (!Updateable) {
// The core processor km_kbp_process_event is only called once per key stroke
// therefore there is no need to preserve context and keyboard actions
if (!isUsingCoreProcessor) {
savedContext = new AppContextWithStores(_td->lpActiveKeyboard->Keyboard->cxStoreArray); // I4370 // I4978
_td->app->SaveContext(savedContext);
}
}
BOOL res = ProcessHook();
if (!Updateable) {
if (!isUsingCoreProcessor) {
if (res) { // I4585 // I4370
// Reset the context if match found
_td->app->RestoreContext(savedContext);
delete savedContext;
savedContext = NULL;
}
}
}
_td->TIPProcessOutput = NULL;
_td->TIPGetContext = NULL;
@ -439,65 +366,6 @@ void AITIP::ReadContext() {
}
}
AppContextWithStores::AppContextWithStores(int nKeyboardOptions) : AppContext() { // I4978
this->nKeyboardOptions = nKeyboardOptions;
KeyboardOptions = new INTKEYBOARDOPTIONS[nKeyboardOptions];
memset(KeyboardOptions, 0, sizeof(INTKEYBOARDOPTIONS) * nKeyboardOptions);
}
AppContextWithStores::~AppContextWithStores() { // I4978
for(DWORD i = 0; i < nKeyboardOptions; i++) {
if(KeyboardOptions[i].Value) delete KeyboardOptions[i].Value;
}
delete KeyboardOptions;
}
void AITIP::SaveContext(AppContextWithStores *savedContext) { // I4370 // I4978
savedContext->CopyFrom(context);
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td || !_td->lpActiveKeyboard || !_td->lpActiveKeyboard->Keyboard) return;
assert(savedContext->nKeyboardOptions == _td->lpActiveKeyboard->Keyboard->cxStoreArray);
for(DWORD i = 0; i < savedContext->nKeyboardOptions; i++) { // I4978
if(_td->lpActiveKeyboard->KeyboardOptions[i].Value != NULL) {
savedContext->KeyboardOptions[i].Value = new WCHAR[wcslen(_td->lpActiveKeyboard->KeyboardOptions[i].Value)+1];
wcscpy_s(savedContext->KeyboardOptions[i].Value, wcslen(_td->lpActiveKeyboard->KeyboardOptions[i].Value)+1, _td->lpActiveKeyboard->KeyboardOptions[i].Value);
}
}
}
void AITIP::RestoreContext(AppContextWithStores *savedContext) { // I4370 // I4978
context->CopyFrom(savedContext);
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td || !_td->lpActiveKeyboard || !_td->lpActiveKeyboard->Keyboard) return;
LPINTKEYBOARDINFO kp = _td->lpActiveKeyboard;
assert(savedContext->nKeyboardOptions == kp->Keyboard->cxStoreArray);
for(DWORD i = 0; i < savedContext->nKeyboardOptions; i++) { // I4978
if(kp->KeyboardOptions[i].Value == NULL && savedContext->KeyboardOptions[i].Value != NULL) {
// Restore the previously saved value as it was reset
kp->KeyboardOptions[i].OriginalStore = kp->Keyboard->dpStoreArray[i].dpString;
kp->Keyboard->dpStoreArray[i].dpString = kp->KeyboardOptions[i].Value = savedContext->KeyboardOptions[i].Value;
savedContext->KeyboardOptions[i].Value = NULL;
} else if(kp->KeyboardOptions[i].Value != NULL && savedContext->KeyboardOptions[i].Value == NULL) {
// Clear the newly saved value back to the default
delete kp->KeyboardOptions[i].Value;
kp->KeyboardOptions[i].Value = NULL;
kp->Keyboard->dpStoreArray[i].dpString = kp->KeyboardOptions[i].OriginalStore;
} else if(kp->KeyboardOptions[i].Value != NULL && savedContext->KeyboardOptions[i].Value != NULL &&
wcscmp(kp->KeyboardOptions[i].Value, savedContext->KeyboardOptions[i].Value) != 0) {
// Restore the previously saved value as it was changed
delete kp->KeyboardOptions[i].Value;
kp->Keyboard->dpStoreArray[i].dpString = kp->KeyboardOptions[i].Value = savedContext->KeyboardOptions[i].Value;
savedContext->KeyboardOptions[i].Value = NULL;
}
}
}
void AITIP::CopyContext(AppContext *savedContext) {
savedContext->CopyFrom(context);
}
@ -722,49 +590,3 @@ void FillStoreOffsets(AIDEBUGINFO *di)
}
di->StoreOffsets[n] = 0xFFFF;
}
BOOL AITIP::QueueDebugInformation(int ItemType, LPGROUP Group, LPKEY Rule, PWSTR fcontext, PWSTR foutput, DWORD_PTR dwExtraFlags)
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return TRUE;
if(!_td->ForceFileName[0]) return TRUE;
SendDebugMessageFormat(0, sdmAIDefault, 0, "AIDebugger::QueueDebugInformation ItemType=%d", ItemType);
AIDEBUGINFO di;
di.cbSize = sizeof(AIDEBUGINFO);
di.ItemType = ItemType; // int
di.Context = fcontext; // PWSTR
di.Rule = Rule; // LPKEY
di.Group = Group; // LPGROUP
di.Output = foutput; // PWSTR
di.Flags = dwExtraFlags; // DWORD
if(di.Rule) FillStoreOffsets(&di);
// data required
// keystroke
// context for rule
// if rule, then output of rule
// match positions for all stores in rule
if(DebugControlled())
SendMessage(GetDebugControlWindow(), WM_KEYMANDEBUG_RULEMATCH, ItemType, (LPARAM) &di);
return TRUE;
}
typedef BOOL(WINAPI *PREFRESHPRESERVEDKEYSFUNC)(BOOL Activating);
void RefreshPreservedKeys(BOOL Activating) {
#ifdef _WIN64
HMODULE hModule = GetModuleHandle("kmtip64");
#else
HMODULE hModule = GetModuleHandle("kmtip");
#endif
if (hModule != NULL) {
PREFRESHPRESERVEDKEYSFUNC pRefreshPreservedKeys = (PREFRESHPRESERVEDKEYSFUNC)GetProcAddress(hModule, "RefreshPreservedKeys");
if (pRefreshPreservedKeys) {
pRefreshPreservedKeys(Activating);
}
}
}

View file

@ -71,11 +71,6 @@ public:
BOOL DebugControlled();
// TODO: 5442 This would be better to called SaveContextWithStores or SaveContextWithKbdOptions
// Will be removed with 5442 when removing window core
void SaveContext(AppContextWithStores *savedContext); // I4370 // I4978
void RestoreContext(AppContextWithStores *savedContext); // I4370 // I4978
/**
* Copy the member context
*
@ -106,7 +101,6 @@ public:
/* Queue and sending functions */
virtual BOOL SendActions(); // I4196
virtual BOOL QueueDebugInformation(int ItemType, LPGROUP Group, LPKEY Rule, PWSTR fcontext, PWSTR foutput, DWORD_PTR dwExtraFlags);
/* TIP interactions */

View file

@ -1,18 +1,18 @@
/*
Name: AIWin2000Unicode
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Documentation:
Description:
Create Date: 22 Jan 2007
Modified Date: 9 Aug 2015
Authors: mcdurdin
Related Files:
Dependencies:
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
Bugs:
Todo:
Notes:
History: 22 Jan 2007 - mcdurdin - Fix for K_NPENTER
13 Jul 2007 - mcdurdin - I934 - Prep fox x64
23 Aug 2007 - mcdurdin - I719 - Fix Alt+LeftShift and Word interactions
@ -43,12 +43,6 @@
#include "pch.h" // I4128 // I4287
#include "serialkeyeventclient.h"
#define KEYMAN_MOREPOST "WM_KMMOREPOST"
#ifndef WM_UNICHAR
#define WM_UNICHAR 0x0109
#define UNICODE_NOCHAR 0xFFFF
#endif
AIWin2000Unicode::AIWin2000Unicode()
{
@ -70,13 +64,13 @@ BOOL AIWin2000Unicode::CanHandleWindow(HWND ahwnd)
}
BOOL AIWin2000Unicode::HandleWindow(HWND ahwnd)
{
{
if(hwnd != ahwnd)
{
hwnd = ahwnd;
hwnd = ahwnd;
context->Reset();
}
return TRUE;
return TRUE;
}
BOOL AIWin2000Unicode::IsWindowHandled(HWND ahwnd)
@ -84,11 +78,11 @@ BOOL AIWin2000Unicode::IsWindowHandled(HWND ahwnd)
return (hwnd == ahwnd);
}
BOOL AIWin2000Unicode::IsUnicode()
{
BOOL AIWin2000Unicode::IsUnicode()
{
BOOL Result = IsWindowUnicode(hwnd);
SendDebugMessageFormat(0, sdmAIDefault, 0, "IsWindowUnicode=%s", Result ? "Yes" : "No");
return Result;
return Result;
}
/* Context functions */
@ -96,7 +90,7 @@ BOOL AIWin2000Unicode::IsUnicode()
void AIWin2000Unicode::ReadContext()
{
}
void AIWin2000Unicode::AddContext(WCHAR ch) //I2436
{
context->Add(ch);
@ -121,7 +115,7 @@ void AIWin2000Unicode::SetContext(const WCHAR* buf)
{
return context->Set(buf);
}
BYTE SavedKbdState[256];
BOOL AIWin2000Unicode::SendActions() // I4196
@ -137,7 +131,7 @@ BOOL AIWin2000Unicode::QueueAction(int ItemType, DWORD dwData)
int result = AppIntegration::QueueAction(ItemType, dwData);
//SendDebugMessageFormat(hwnd, sdmAIDefault, 0, "App::QueueAction ItemType=%d dwData=%x", ItemType, dwData);
switch(ItemType)
{
case QIT_VKEYDOWN:
@ -166,27 +160,16 @@ BOOL AIWin2000Unicode::QueueAction(int ItemType, DWORD dwData)
return result;
}
BOOL AIWin2000Unicode::QueueDebugInformation(int ItemType, LPGROUP Group, LPKEY Rule, PWSTR fcontext, PWSTR foutput, DWORD_PTR dwExtraFlags)
{
UNREFERENCED_PARAMETER(ItemType);
UNREFERENCED_PARAMETER(Group);
UNREFERENCED_PARAMETER(Rule);
UNREFERENCED_PARAMETER(fcontext);
UNREFERENCED_PARAMETER(foutput);
UNREFERENCED_PARAMETER(dwExtraFlags);
return TRUE;
}
// I1512 - SendInput with VK_PACKET for greater robustness
BOOL AIWin2000Unicode::PostKeys()
{
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) {
return FALSE;
}
if(QueueSize == 0)
if(QueueSize == 0)
{
return TRUE;
}
@ -208,7 +191,7 @@ BOOL AIWin2000Unicode::PostKeys()
switch(Queue[n].ItemType) {
case QIT_VKEYDOWN:
if((Queue[n].dwData & QVK_KEYMASK) == 0x05) Queue[n].dwData = (Queue[n].dwData & QVK_FLAGMASK) | VK_RETURN; // I649 // I3438
/* 6.0.153.0: Fix repeat state for virtual keys */
if((Queue[n].dwData & QVK_KEYMASK) <= VK__MAX) // I3438
@ -277,7 +260,6 @@ BOOL AIWin2000Unicode::PostKeys()
break;
case QIT_BACK:
if(Queue[n].dwData & BK_DEADKEY) break;
if(Addin_ProcessBackspace(hwnd)) break;
pInputs[i].type = INPUT_KEYBOARD;
pInputs[i].ki.wVk = VK_BACK;

View file

@ -1,18 +1,18 @@
/*
Name: aiWin2000Unicode
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Documentation:
Description:
Create Date: 27 Jan 2009
Modified Date: 23 Jun 2014
Authors: mcdurdin
Related Files:
Dependencies:
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
Bugs:
Todo:
Notes:
History: 27 Jan 2009 - mcdurdin - I1797 - Add fallback for AIWin2000 app integration
11 Dec 2009 - mcdurdin - I934 - x64 - Initial version
24 Jun 2010 - mcdurdin - I2436 - Add space to context for AIWin2000Unicode when not matched
@ -43,7 +43,7 @@ public:
virtual BOOL QueueAction(int ItemType, DWORD dwData);
/* Information functions */
virtual BOOL CanHandleWindow(HWND ahwnd);
virtual BOOL IsWindowHandled(HWND ahwnd);
virtual BOOL HandleWindow(HWND ahwnd);
@ -59,9 +59,8 @@ public:
virtual void SetContext(const WCHAR* buf);
/* Queue and sending functions */
virtual BOOL SendActions(); // I4196
virtual BOOL QueueDebugInformation(int ItemType, LPGROUP Group, LPKEY Rule, PWSTR fcontext, PWSTR foutput, DWORD_PTR dwExtraFlags);
};
#endif

View file

@ -43,22 +43,6 @@ typedef struct
#define QIT_CAPSLOCK 8
#define QIT_INVALIDATECONTEXT 9
// QueueDebugInformation ItemTypes
#define QID_BEGIN_UNICODE 0
#define QID_BEGIN_ANSI 1
#define QID_GROUP_ENTER 2
#define QID_GROUP_EXIT 3
#define QID_RULE_ENTER 4
#define QID_RULE_EXIT 5
#define QID_MATCH_ENTER 6
#define QID_MATCH_EXIT 7
#define QID_NOMATCH_ENTER 8
#define QID_NOMATCH_EXIT 9
#define QID_END 10
#define QID_FLAG_RECURSIVE_OVERFLOW 0x0001
#define QID_FLAG_NOMATCH 0x0002
#define QVK_EXTENDED 0x00010000 // Flag for QIT_VKEYDOWN to indicate an extended key
#define QVK_KEYMASK 0x0000FFFF
#define QVK_FLAGMASK 0xFFFF0000
@ -178,15 +162,6 @@ public:
};
class AppContextWithStores : public AppContext // I4978
{
public:
AppContextWithStores(int nKeyboardOptions);
~AppContextWithStores();
DWORD nKeyboardOptions;
LPINTKEYBOARDOPTIONS KeyboardOptions;
};
class AppIntegration:public AppActionQueue
{
protected:
@ -214,7 +189,6 @@ public:
/* Queue and sending functions */
virtual BOOL QueueDebugInformation(int ItemType, LPGROUP Group, LPKEY Rule, PWSTR fcontext, PWSTR foutput, DWORD_PTR dwExtraFlags) = 0;
void SetCurrentShiftState(int ShiftFlags) { FShiftFlags = ShiftFlags; }
virtual BOOL SendActions() = 0; // I4196
};

View file

@ -93,34 +93,6 @@ static LPIMDLL AddIMDLL(LPINTKEYBOARDINFO lpkbi, LPSTR kbdpath, LPSTR dllfilenam
return imd;
}
/* Add a dll hook function to the list of hook functions associated with a single dll */
static BOOL AddIMDLLHook(LPIMDLL imd, LPSTR funcname, DWORD storeno, PWCHAR *dpString)
{
//SendDebugMessageFormat(0, sdmKeyboard, 0, "AddIMDLLHook: Enter");
/* Get the procedure address for the function */
IMDLLHOOKProc dhp = (IMDLLHOOKProc) GetProcAddress(imd->hModule, funcname);
if(!dhp) return FALSE;
/* Add the function to the list of functions in the DLL */
LPIMDLLHOOK hooks = new IMDLLHOOK[imd->nHooks+1];
if(imd->nHooks > 0)
{
memcpy(hooks, imd->Hooks, sizeof(IMDLLHOOK) * imd->nHooks);
delete imd->Hooks;
}
imd->Hooks = hooks;
strncpy(imd->Hooks[imd->nHooks].name, funcname, 31);
imd->Hooks[imd->nHooks].name[31] = 0;
imd->Hooks[imd->nHooks].storeno = storeno;
imd->Hooks[imd->nHooks].function = dhp;
*dpString = (PWCHAR) &imd->Hooks[imd->nHooks++];
//SendDebugMessageFormat(0, sdmKeyboard, 0, "AddIMDLLHook: Exit");
return TRUE;
}
static km_kbp_action_item*
kmnToCoreActionItem(int ItemType, DWORD dwData, WORD wVkey) {
@ -232,55 +204,6 @@ BOOL CallbackDLLs(LPINTKEYBOARDINFO lpkbi, PSTR cmd)
return TRUE;
}
/* Load the dlls associated with a keyboard */
BOOL LoadDLLs(LPINTKEYBOARDINFO lpkbi)
{
char fullname[_MAX_PATH];
//SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLs: Enter");
if(lpkbi->nIMDLLs > 0) if(!UnloadDLLs(lpkbi)) return FALSE;
if (!GetKeyboardFileName(lpkbi->Name, fullname, _MAX_PATH)) {
SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLs: Filename not found[%s]", lpkbi->Name);
return FALSE;
}
if (!lpkbi->Keyboard) {
SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLs: Keyboard is null");
return FALSE;
}
for(DWORD i = 0; i < lpkbi->Keyboard->cxStoreArray; i++)
{
LPSTORE s = &lpkbi->Keyboard->dpStoreArray[i];
if(s->dwSystemID == TSS_CALLDEFINITION)
{
/* Break the store string into components */
PCHAR p = wstrtostr(s->dpString), q, r, context;
q = strtok_s(p, ":", &context);
r = strtok_s(NULL, ":", &context);
if(!q || !r)
{
s->dwSystemID = TSS_CALLDEFINITION_LOADFAILED;
delete[] p;
continue;
}
LPIMDLL imd = AddIMDLL(lpkbi, fullname, q);
if(imd && AddIMDLLHook(imd, r, i, &s->dpString)) s->dwSystemID = TSS_CALLDEFINITION;
else s->dwSystemID = TSS_CALLDEFINITION_LOADFAILED;
delete[] p;
}
}
//SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLs: Exit");
return TRUE;
}
// Both Core and Window keyboard processor can use this function
BOOL UnloadDLLs(LPINTKEYBOARDINFO lpkbi)
{
@ -302,7 +225,7 @@ BOOL UnloadDLLs(LPINTKEYBOARDINFO lpkbi)
lpkbi->IMDLLs = NULL;
lpkbi->nIMDLLs = 0;
if (Globals::get_CoreIntegration() && lpkbi->lpCoreKeyboardState) {
if (lpkbi->lpCoreKeyboardState) {
km_kbp_state_imx_deregister_callback(lpkbi->lpCoreKeyboardState);
}
return TRUE;
@ -330,26 +253,6 @@ BOOL DeactivateDLLs(LPINTKEYBOARDINFO lpkbi)
return TRUE;
}
void CallDLL(LPINTKEYBOARDINFO lpkbi, DWORD storenum)
{
//SendDebugMessageFormat(0, sdmKeyboard, 0, "CallDll: Enter");
if (!lpkbi->Keyboard) return;
if(storenum >= lpkbi->Keyboard->cxStoreArray) return;
LPSTORE s = &lpkbi->Keyboard->dpStoreArray[storenum];
if(s->dwSystemID != TSS_CALLDEFINITION) return;
if(s->dpString == NULL) return;
LPIMDLLHOOK imdh = (LPIMDLLHOOK) s->dpString;
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return;
if(_td->TIPFUpdateable) { // I4452
(*imdh->function)(_td->state.msg.hwnd, _td->state.vkey, _td->state.charCode, Globals::get_ShiftState());
}
//SendDebugMessageFormat(0, sdmKeyboard, 0, "CallDll: Exit");
}
// The callback function called by the Core Keyboardprocessor
extern "C" uint8_t IM_CallBackCore(km_kbp_state *km_state, uint32_t UniqueStoreNo, void *callbackObject) {
//SendDebugMessageFormat(0, sdmKeyboard, 0, "IM_CallBackCore: Enter");
@ -402,90 +305,84 @@ extern "C" BOOL _declspec(dllexport) WINAPI KMSetOutput(PWSTR buf, DWORD backlen
if (!_td->app)
return FALSE;
if (!Globals::get_CoreIntegration()) { // TODO: 5442 Remove If and fix indent
while (backlen-- > 0)
_td->app->QueueAction(QIT_BACK, BK_DEFAULT);
while (*buf)
_td->app->QueueAction(QIT_CHAR, *buf++);
return TRUE;
} else {
if (!_td->lpActiveKeyboard || !_td->lpActiveKeyboard->lpCoreKeyboardState) {
SendDebugMessageFormat(0, sdmKeyboard, 0, "KMSetOutputCore: no active state");
return FALSE;
}
DWORD numActions = backlen + (DWORD)wcslen(buf);
DWORD idx = 0;
km_kbp_action_item *actionItems = new km_kbp_action_item[numActions + 1];
// The actions sent to the core processor need to set the expected_type
// correctly. To do this need to check the context as we process the
// backspaces.
km_kbp_context_item *citems = nullptr;
if (KM_KBP_STATUS_OK != kbp_state_get_intermediate_context(_td->lpActiveKeyboard->lpCoreKeyboardState, &citems)) {
delete[] actionItems;
return FALSE;
}
DWORD context_length = (DWORD)km_kbp_context_item_list_size(citems);
WCHAR *contextString = new WCHAR[(context_length * 3) + 1]; // *3 if every context item was a deadkey
if (!ContextItemToAppContext(citems, contextString, context_length)) {
km_kbp_context_items_dispose(citems);
delete[] contextString;
delete[] actionItems;
return FALSE;
}
km_kbp_context_items_dispose(citems);
AppContext context;
context.Set(contextString);
delete[] contextString;
while (backlen-- > 0) {
actionItems[idx].type = KM_KBP_IT_BACK;
WCHAR *CodeUnitPtr;
const int DeadKeyLength = 3;
const int SurrogateLength = 2;
const int SingleCharLength = 1;
if (context.CharIsDeadkey()) {
CodeUnitPtr = context.BufMax(DeadKeyLength);
CodeUnitPtr += 2;
actionItems[idx].backspace.expected_type = KM_KBP_BT_MARKER;
actionItems[idx].backspace.expected_value = (uintptr_t)*CodeUnitPtr;
} else if (context.CharIsSurrogatePair()) {
CodeUnitPtr = context.BufMax(SurrogateLength);
actionItems[idx].backspace.expected_type = KM_KBP_BT_CHAR;
actionItems[idx].backspace.expected_value = (DWORD)Uni_SurrogateToUTF32(*CodeUnitPtr, *(CodeUnitPtr + 1));
} else if (!context.IsEmpty()) {
CodeUnitPtr = context.BufMax(SingleCharLength);
actionItems[idx].backspace.expected_type = KM_KBP_BT_CHAR;
actionItems[idx].backspace.expected_value = (DWORD)*CodeUnitPtr;
} else {
actionItems[idx].backspace.expected_type = KM_KBP_BT_UNKNOWN;
actionItems[idx].backspace.expected_value = 0;
}
context.Delete();
idx++;
}
while (*buf) {
actionItems[idx].type = KM_KBP_IT_CHAR;
if (Uni_IsSurrogate1(*buf) && Uni_IsSurrogate2(*(buf + 1))) {
actionItems[idx].character = Uni_SurrogateToUTF32(*buf, *(buf + 1));
buf++;
} else {
actionItems[idx].character = (DWORD)(*buf);
}
buf++;
idx++;
}
actionItems[idx].type = KM_KBP_IT_END;
if (KM_KBP_STATUS_OK != km_kbp_state_queue_action_items(_td->lpActiveKeyboard->lpCoreKeyboardState, actionItems)) {
delete[] actionItems;
return FALSE;
}
delete[] actionItems;
//SendDebugMessageFormat(0, sdmKeyboard, 0, "KMSetOutputCore: Exit");
return TRUE;
if (!_td->lpActiveKeyboard || !_td->lpActiveKeyboard->lpCoreKeyboardState) {
SendDebugMessageFormat(0, sdmKeyboard, 0, "KMSetOutputCore: no active state");
return FALSE;
}
DWORD numActions = backlen + (DWORD)wcslen(buf);
DWORD idx = 0;
km_kbp_action_item *actionItems = new km_kbp_action_item[numActions + 1];
// The actions sent to the core processor need to set the expected_type
// correctly. To do this need to check the context as we process the
// backspaces.
km_kbp_context_item *citems = nullptr;
if (KM_KBP_STATUS_OK != kbp_state_get_intermediate_context(_td->lpActiveKeyboard->lpCoreKeyboardState, &citems)) {
delete[] actionItems;
return FALSE;
}
DWORD context_length = (DWORD)km_kbp_context_item_list_size(citems);
WCHAR *contextString = new WCHAR[(context_length * 3) + 1]; // *3 if every context item was a deadkey
if (!ContextItemToAppContext(citems, contextString, context_length)) {
km_kbp_context_items_dispose(citems);
delete[] contextString;
delete[] actionItems;
return FALSE;
}
km_kbp_context_items_dispose(citems);
AppContext context;
context.Set(contextString);
delete[] contextString;
while (backlen-- > 0) {
actionItems[idx].type = KM_KBP_IT_BACK;
WCHAR *CodeUnitPtr;
const int DeadKeyLength = 3;
const int SurrogateLength = 2;
const int SingleCharLength = 1;
if (context.CharIsDeadkey()) {
CodeUnitPtr = context.BufMax(DeadKeyLength);
CodeUnitPtr += 2;
actionItems[idx].backspace.expected_type = KM_KBP_BT_MARKER;
actionItems[idx].backspace.expected_value = (uintptr_t)*CodeUnitPtr;
} else if (context.CharIsSurrogatePair()) {
CodeUnitPtr = context.BufMax(SurrogateLength);
actionItems[idx].backspace.expected_type = KM_KBP_BT_CHAR;
actionItems[idx].backspace.expected_value = (DWORD)Uni_SurrogateToUTF32(*CodeUnitPtr, *(CodeUnitPtr + 1));
} else if (!context.IsEmpty()) {
CodeUnitPtr = context.BufMax(SingleCharLength);
actionItems[idx].backspace.expected_type = KM_KBP_BT_CHAR;
actionItems[idx].backspace.expected_value = (DWORD)*CodeUnitPtr;
} else {
actionItems[idx].backspace.expected_type = KM_KBP_BT_UNKNOWN;
actionItems[idx].backspace.expected_value = 0;
}
context.Delete();
idx++;
}
while (*buf) {
actionItems[idx].type = KM_KBP_IT_CHAR;
if (Uni_IsSurrogate1(*buf) && Uni_IsSurrogate2(*(buf + 1))) {
actionItems[idx].character = Uni_SurrogateToUTF32(*buf, *(buf + 1));
buf++;
} else {
actionItems[idx].character = (DWORD)(*buf);
}
buf++;
idx++;
}
actionItems[idx].type = KM_KBP_IT_END;
if (KM_KBP_STATUS_OK != km_kbp_state_queue_action_items(_td->lpActiveKeyboard->lpCoreKeyboardState, actionItems)) {
delete[] actionItems;
return FALSE;
}
delete[] actionItems;
//SendDebugMessageFormat(0, sdmKeyboard, 0, "KMSetOutputCore: Exit");
return TRUE;
}
extern "C" BOOL _declspec(dllexport) WINAPI KMQueueAction(int ItemType, DWORD dwData) {
@ -496,61 +393,53 @@ extern "C" BOOL _declspec(dllexport) WINAPI KMQueueAction(int ItemType, DWORD dw
if (!_td->app)
return FALSE;
if (!Globals::get_CoreIntegration()) {
return _td->app->QueueAction(ItemType, dwData); // TODO: 5442 Remove
} else {
if (!_td->lpActiveKeyboard || !_td->lpActiveKeyboard->lpCoreKeyboardState) {
return FALSE;
}
km_kbp_action_item *actionItem = kmnToCoreActionItem(ItemType, dwData, _td->state.vkey);
km_kbp_status_codes error_status =
(km_kbp_status_codes)km_kbp_state_queue_action_items(_td->lpActiveKeyboard->lpCoreKeyboardState, actionItem);
if (error_status != KM_KBP_STATUS_OK) {
delete[] actionItem;
SendDebugMessageFormat(0, sdmKeyboard, 0, "KMQueueAction: Error core queue_action_items error status:[%lu]",error_status);
return FALSE;
}
delete[] actionItem;
//SendDebugMessageFormat(0, sdmKeyboard, 0, "KMQueueActionCore: Exit");
return TRUE;
if (!_td->lpActiveKeyboard || !_td->lpActiveKeyboard->lpCoreKeyboardState) {
return FALSE;
}
km_kbp_action_item *actionItem = kmnToCoreActionItem(ItemType, dwData, _td->state.vkey);
km_kbp_status_codes error_status =
(km_kbp_status_codes)km_kbp_state_queue_action_items(_td->lpActiveKeyboard->lpCoreKeyboardState, actionItem);
if (error_status != KM_KBP_STATUS_OK) {
delete[] actionItem;
SendDebugMessageFormat(0, sdmKeyboard, 0, "KMQueueAction: Error core queue_action_items error status:[%lu]",error_status);
return FALSE;
}
delete[] actionItem;
//SendDebugMessageFormat(0, sdmKeyboard, 0, "KMQueueActionCore: Exit");
return TRUE;
}
extern "C" BOOL _declspec(dllexport) WINAPI KMGetContext(PWSTR buf, DWORD len)
{
//SendDebugMessageFormat(0, sdmKeyboard, 0, "KMGetContext: Enter");
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
if(!_td->app) return FALSE;
// TODO: 5442 KMGetContext is already public call (even though it is pointer) Rather then making a new KMGetContextCore
// This has been modified to check for core processor once we move to core processor the old Windows Platmform calling of
// ContextBuff can be removed
//
if(!Globals::get_CoreIntegration()){
PWSTR q = _td->app->ContextBufMax(len);
if (!q)
return FALSE; // context buf does not exist
wcscpy_s(buf, len + 1, q); // I3091
return TRUE;
} else {
if (!_td->lpActiveKeyboard || !_td->lpActiveKeyboard->lpCoreKeyboardState) {
return FALSE;
}
km_kbp_context_item *citems = nullptr;
if (KM_KBP_STATUS_OK != kbp_state_get_intermediate_context(_td->lpActiveKeyboard->lpCoreKeyboardState, &citems)) {
return FALSE;
}
if (!ContextItemToAppContext(citems, buf, len)) {
km_kbp_context_items_dispose(citems);
return FALSE;
}
km_kbp_context_items_dispose(citems);
//SendDebugMessageFormat(0, sdmKeyboard, 0, "KMGetContext: Exit");
return TRUE;
if(!_td) {
return FALSE;
}
if(!_td->app) {
return FALSE;
}
if (!_td->lpActiveKeyboard || !_td->lpActiveKeyboard->lpCoreKeyboardState) {
return FALSE;
}
km_kbp_context_item *citems = nullptr;
if (KM_KBP_STATUS_OK != kbp_state_get_intermediate_context(_td->lpActiveKeyboard->lpCoreKeyboardState, &citems)) {
return FALSE;
}
if (!ContextItemToAppContext(citems, buf, len)) {
km_kbp_context_items_dispose(citems);
return FALSE;
}
km_kbp_context_items_dispose(citems);
//SendDebugMessageFormat(0, sdmKeyboard, 0, "KMGetContext: Exit");
return TRUE;
}
extern "C" BOOL _declspec(dllexport) WINAPI KMDisplayIM(HWND hwnd, BOOL FShowAlways)
@ -650,8 +539,8 @@ BOOL IsIMWindow(HWND hwnd)
/* Add a dll hook function to the list of hook functions associated with a single dll */
static BOOL
AddIMDLLHookCore(LPIMDLL imd, LPSTR funcname, DWORD storeno) {
//SendDebugMessageFormat(0, sdmKeyboard, 0, "AddIMDLLHookCore: Enter");
AddIMDLLHook(LPIMDLL imd, LPSTR funcname, DWORD storeno) {
//SendDebugMessageFormat(0, sdmKeyboard, 0, "AddIMDLLHook: Enter");
/* Get the procedure address for the function */
IMDLLHOOKProc dhp = (IMDLLHOOKProc)GetProcAddress(imd->hModule, funcname);
if (!dhp)
@ -670,15 +559,15 @@ AddIMDLLHookCore(LPIMDLL imd, LPSTR funcname, DWORD storeno) {
imd->Hooks[imd->nHooks].storeno = storeno;
imd->Hooks[imd->nHooks].function = dhp;
imd->nHooks++;
//SendDebugMessageFormat(0, sdmKeyboard, 0, "AddIMDLLHookCore: Exit");
//SendDebugMessageFormat(0, sdmKeyboard, 0, "AddIMDLLHook: Exit");
return TRUE;
}
/* Load the dlls associated with a keyboard */
BOOL
LoadDLLsCore(LPINTKEYBOARDINFO lpkbi) {
LoadDLLs(LPINTKEYBOARDINFO lpkbi) {
char fullname[_MAX_PATH];
//SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLsCore: Enter");
//SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLs: Enter");
if (lpkbi->nIMDLLs > 0)
if (!UnloadDLLs(lpkbi))
@ -694,11 +583,11 @@ LoadDLLsCore(LPINTKEYBOARDINFO lpkbi) {
BOOL result = false;
for (; imx_list->library_name; ++imx_list) {
LPIMDLL imd = AddIMDLL(lpkbi, fullname, wstrtostr(reinterpret_cast<LPCWSTR>(imx_list->library_name)));
if (imd && AddIMDLLHookCore(imd, wstrtostr(reinterpret_cast<LPCWSTR>(imx_list->function_name)), imx_list->imx_id)) {
if (imd && AddIMDLLHook(imd, wstrtostr(reinterpret_cast<LPCWSTR>(imx_list->function_name)), imx_list->imx_id)) {
result = TRUE;
}
else {
SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLsCore: Error Loading Library name [%s], Function name [%s]",
SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLs: Error Loading Library name [%s], Function name [%s]",
wstrtostr(reinterpret_cast<PCWSTR>(imx_list->library_name)),
wstrtostr(reinterpret_cast<PCWSTR>(imx_list->function_name)));
}
@ -707,7 +596,7 @@ LoadDLLsCore(LPINTKEYBOARDINFO lpkbi) {
if (result) {
km_kbp_state_imx_register_callback(lpkbi->lpCoreKeyboardState, IM_CallBackCore, (void *)lpkbi);
}
//SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLsCore: Exit");
//SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLs: Exit");
return TRUE;
}

View file

@ -19,24 +19,19 @@
#ifndef __CALLDLL_H
#define __CALLDLL_H
BOOL LoadDLLs(LPINTKEYBOARDINFO lpkbi);
BOOL UnloadDLLs(LPINTKEYBOARDINFO lpkbi);
BOOL DeactivateDLLs(LPINTKEYBOARDINFO lpkbi);
BOOL ActivateDLLs(LPINTKEYBOARDINFO lpkbi);
// TODO: 5444 This will become the only LoadDLLs function
/**
* Load the all the dlls used by the current keyboard
* @param lpkbi The keyboard for which to load the dlls
* @return BOOL True on success
*/
BOOL LoadDLLsCore(LPINTKEYBOARDINFO lpkbi);
BOOL LoadDLLs(LPINTKEYBOARDINFO lpkbi);
BOOL UnloadDLLs(LPINTKEYBOARDINFO lpkbi);
BOOL DeactivateDLLs(LPINTKEYBOARDINFO lpkbi);
BOOL ActivateDLLs(LPINTKEYBOARDINFO lpkbi);
BOOL IsIMWindow(HWND hwnd);
void CallDLL(LPINTKEYBOARDINFO lpkbi, DWORD storenum);
// Callback function used by the core processor to call out to 3rd Party Library functions
extern "C" uint8_t IM_CallBackCore(km_kbp_state *km_state, uint32_t UniqueStoreNo, void *callbackObject);

View file

@ -29,73 +29,3 @@
BOOL IsCapsLockOn(void) {
return GetKeyState(VK_CAPITAL) & 1;
}
void ResetCapsLock(void)
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if (!_td) return;
if (!_td->lpActiveKeyboard) return;
if (!_td->lpActiveKeyboard->Keyboard) return;
SendDebugMessageFormat(0, sdmGlobal, 0, "ResetCapsLock: enter");
if (_td->lpActiveKeyboard->Keyboard->dwFlags & KF_CAPSALWAYSOFF)
{
SendDebugMessageFormat(0, sdmGlobal, 0, "ResetCapsLock: caps lock should be always off");
if (IsCapsLockOn())
{
SendDebugMessageFormat(0, sdmGlobal, 0, "ResetCapsLock: caps lock is on, switching off caps lock");
keybd_event(VK_CAPITAL, 0x3A, 0, 0);
keybd_event(VK_CAPITAL, 0x3A, 0 | KEYEVENTF_KEYUP, 0);
}
}
SendDebugMessageFormat(0, sdmGlobal, 0, "ResetCapsLock: exit");
}
void KeyCapsLockPress(BOOL FIsUp) // I3284 - void // I3529
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if (!_td) return;
if (!_td->lpActiveKeyboard) return; // pass through to window
if (!_td->lpActiveKeyboard->Keyboard) return;
if (_td->lpActiveKeyboard->Keyboard->dwFlags & KF_CAPSONONLY)
{
SendDebugMessageFormat(0, sdmAIDefault, 0, "KeyCapsLockPress: KF_CAPSONONLY: FIsUp=%d CapsState=%d", FIsUp, IsCapsLockOn());
if (FIsUp && !IsCapsLockOn()) // I267 - 24/11/2006 invert GetKeyState test
{
keybd_event(VK_CAPITAL, SCAN_FLAG_KEYMAN_KEY_EVENT, 0, 0);
keybd_event(VK_CAPITAL, SCAN_FLAG_KEYMAN_KEY_EVENT, KEYEVENTF_KEYUP, 0);
}
}
else if (_td->lpActiveKeyboard->Keyboard->dwFlags & KF_CAPSALWAYSOFF)
{
SendDebugMessageFormat(0, sdmAIDefault, 0, "KeyCapsLockPress: KF_CAPSALWAYSOFF: FIsUp=%d CapsState=%d", FIsUp, IsCapsLockOn());
if (!FIsUp && IsCapsLockOn())
{ // I267 - 24/11/2006 invert GetKeyState test
keybd_event(VK_CAPITAL, SCAN_FLAG_KEYMAN_KEY_EVENT, KEYEVENTF_KEYUP, 0);
keybd_event(VK_CAPITAL, SCAN_FLAG_KEYMAN_KEY_EVENT, 0, 0);
}
}
}
void KeyShiftPress(BOOL FIsUp) // I3284 - void // I3529
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if (!_td) return;
if (!_td->lpActiveKeyboard) return; // pass through to window
if (!_td->lpActiveKeyboard->Keyboard) return;
if (_td->lpActiveKeyboard->Keyboard->dwFlags & KF_SHIFTFREESCAPS)
{
SendDebugMessageFormat(0, sdmAIDefault, 0, "KeyShiftPress: KF_SHIFTFREESCAPS: FIsUp=%d CapsState=%d", FIsUp, IsCapsLockOn());
if (!FIsUp && IsCapsLockOn())
{
keybd_event(VK_CAPITAL, SCAN_FLAG_KEYMAN_KEY_EVENT, 0, 0);
keybd_event(VK_CAPITAL, SCAN_FLAG_KEYMAN_KEY_EVENT, KEYEVENTF_KEYUP, 0);
}
}
}

View file

@ -20,8 +20,5 @@
#define __CAPSSTATE_H
BOOL IsCapsLockOn(void);
void ResetCapsLock(void);
void KeyCapsLockPress(BOOL FIsUp);
void KeyShiftPress(BOOL FIsUp);
#endif

View file

@ -151,7 +151,6 @@ public:
static BOOL get_debug_KeymanLog();
static BOOL get_debug_ToConsole();
static BOOL get_CoreIntegration();
static void LoadDebugSettings();
};
@ -216,8 +215,6 @@ typedef struct tagKEYMAN64THREADDATA
FInitialised,
FInitialising;
char ForceFileName[MAX_PATH];
DWORD ActiveKeymanID;
/* TIP Globals */

View file

@ -1,18 +1,18 @@
/*
Name: glossary
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Documentation:
Description:
Create Date: 20 Jul 2008
Modified Date: 28 May 2014
Authors: mcdurdin
Related Files:
Dependencies:
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
Bugs:
Todo:
Notes:
History: 20 Jul 2008 - mcdurdin - I1498 - Fix keyboard switching for Shadow keyboards on Vista+
20 Jul 2008 - mcdurdin - I1546 - Fix language switch with ids >= x80000000
20 Jul 2008 - mcdurdin - I1545 - Fix registry leak
@ -38,9 +38,9 @@ BOOL HKLIsIME(HKL hkl) // I1498 - fix keyboard switching for shadow keyboards o
if( (GetVersion() & 0xFF) >= 6 ) return FALSE;
if( (GetVersion() & 0x8000000) == 0x8000000 || (GetVersion() & 0xFF) == 4 )
r = GetSystemMetrics(SM_DBCSENABLED);
else
else
r = GetSystemMetrics(SM_IMMENABLED);
return r && ImmIsIME(hkl);
}
#pragma warning(default: 4996)
@ -99,12 +99,12 @@ DWORD HKLToKeyboardID(HKL hkl)
return (DWORD) LOWORD(hkl);
}
for(len=16, n = i = 0; RegEnumKeyEx(hkey, i, str, &len, 0, NULL, NULL, &ft) == ERROR_SUCCESS;
len = 16, i++, n=0)
for(len=16, n = i = 0; RegEnumKeyEx(hkey, i, str, &len, 0, NULL, NULL, &ft) == ERROR_SUCCESS;
len = 16, i++, n=0)
{
RegOpenKeyEx(hkey, str, NULL, KEY_READ, &hsubkey);
len = 16;
if(RegQueryValueEx(hsubkey, REGSZ_LayoutID, NULL, NULL, (LPBYTE) str2, &len) == ERROR_SUCCESS)
if(RegQueryValueEx(hsubkey, REGSZ_LayoutID, NULL, NULL, (LPBYTE) str2, &len) == ERROR_SUCCESS)
{
n = strtoul(str2, NULL, 16); // I1546
if(n == LayoutID)
@ -120,7 +120,7 @@ DWORD HKLToKeyboardID(HKL hkl)
}
RegCloseKey(hkey);
//SendDebugMessageFormat(0, sdmGlobal, 0, "HKLToKeyboardID: fails[2], return LOWORD(hkl)=%x", LOWORD(hkl));
return (DWORD) LOWORD(hkl); // should never happen
}
@ -143,11 +143,11 @@ WORD HKLToLayoutNumber(HKL hkl)
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSZ_SystemKeyboardLayouts, NULL, KEY_READ, &hkey) != ERROR_SUCCESS)
return 0;
for(len=16, n = i = 0; RegEnumKeyEx(hkey, i, str, &len, 0, NULL, NULL, &ft) == ERROR_SUCCESS; len = 16, i++, n=0)
for(len=16, n = i = 0; RegEnumKeyEx(hkey, i, str, &len, 0, NULL, NULL, &ft) == ERROR_SUCCESS; len = 16, i++, n=0)
{
RegOpenKeyEx(hkey, str, NULL, KEY_READ, &hsubkey);
len = 16;
if(RegQueryValueEx(hsubkey, REGSZ_LayoutID, NULL, NULL, (LPBYTE) str2, &len) == ERROR_SUCCESS)
if(RegQueryValueEx(hsubkey, REGSZ_LayoutID, NULL, NULL, (LPBYTE) str2, &len) == ERROR_SUCCESS)
{
if(strtoul(str2, NULL, 16) == LayoutID) break; // strtoul - I1546
}
@ -166,37 +166,3 @@ WORD HKLToLayoutID(HKL hkl)
return HIWORD(hkl) & 0x0FFF;
}
DWORD EthnologueCodeToKeymanID(DWORD EthCode)
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return KEYMANID_NONKEYMAN;
for(int i = 0; i < _td->nKeyboards; i++)
{
if(!_td->lpKeyboards[i].Keyboard && !LoadlpKeyboard(i))
{
SendDebugMessageFormat(0,sdmGlobal,0,
"EthnologueCodeToKeymanID: Unable to load keyboard %s", _td->lpKeyboards[i].Name);
return KEYMANID_NONKEYMAN;
}
PWSTR ps = GetSystemStore(_td->lpKeyboards[i].Keyboard, TSS_ETHNOLOGUECODE);
if(ps)
{
SendDebugMessageFormat(0,sdmGlobal,0,"EthnologueCodeToKeymanID: %s %ws %x", _td->lpKeyboards[i].Name, ps, EthnologueStringCodeToDWord(ps));
if(EthnologueStringCodeToDWord(ps) == EthCode) return _td->lpKeyboards[i].KeymanID;
}
}
return KEYMANID_NONKEYMAN;
}
DWORD EthnologueStringCodeToDWord(PWSTR EthCode)
{
if(wcslen(EthCode) < 3 || wcslen(EthCode) > 4) return (DWORD)-1;
return (LOBYTE(EthCode[0])) |
(LOBYTE(EthCode[1]) << 8) |
(LOBYTE(EthCode[2]) << 16) |
(LOBYTE(EthCode[3]) << 24);
}

View file

@ -288,9 +288,6 @@ static BOOL
f_debug_KeymanLog = FALSE,
f_debug_ToConsole = FALSE;
static BOOL
f_CoreIntegration = TRUE;
#pragma data_seg()
/***************************************************************************/
@ -365,8 +362,6 @@ BOOL Globals::get_MnemonicDeadkeyConversionMode() { return f_MnemonicDeadkeyConv
BOOL Globals::get_debug_KeymanLog() { return f_debug_KeymanLog; }
BOOL Globals::get_debug_ToConsole() { return f_debug_ToConsole; }
BOOL Globals::get_CoreIntegration() { return f_CoreIntegration; }
void Globals::SetBaseKeyboardName(wchar_t *baseKeyboardName, wchar_t *baseKeyboardNameAlt) { // I4583
wcscpy_s(f_BaseKeyboardName, baseKeyboardName);
wcscpy_s(f_BaseKeyboardNameAlt, baseKeyboardNameAlt);
@ -385,9 +380,6 @@ void Globals::SetBaseKeyboardFlags(char *baseKeyboard, BOOL simulateAltGr, BOOL
be changed until Keyman is restarted.
*/
BOOL Globals::InitSettings() {
/* Check for common core vs windows core */
f_CoreIntegration = Reg_GetDebugFlag(REGSZ_Flag_UseKeymanCore, TRUE);
SendDebugMessageFormat(0, sdmAIDefault, 0, "Globals::InitSettings - Coreintegration set in '" REGSZ_Flag_UseKeymanCore "' to %x", f_CoreIntegration);
f_vk_prefix = _VK_PREFIX_DEFAULT;
RegistryReadOnly reg(HKEY_LOCAL_MACHINE);
if (reg.OpenKeyReadOnly(REGSZ_KeymanLM) &&

View file

@ -19,10 +19,8 @@
*/
#include "pch.h"
void IntSaveKeyboardOption(LPCSTR key, LPINTKEYBOARDINFO kp, int nStoreToSave);
BOOL IntLoadKeyboardOptions(LPCSTR key, LPINTKEYBOARDINFO kp);
BOOL IntLoadKeyboardOptionsCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_kbp_state* const state);
void IntSaveKeyboardOptionREGCore(LPCSTR REGKey, LPINTKEYBOARDINFO kp, LPCWSTR key, LPWSTR value);
BOOL IntLoadKeyboardOptionsRegistrytoCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_kbp_state* const state);
void IntSaveKeyboardOptionCoretoRegistry(LPCSTR REGKey, LPINTKEYBOARDINFO kp, LPCWSTR key, LPWSTR value);
static km_kbp_cp* CloneKMKBPCP(const km_kbp_cp* cp) {
LPCWSTR buf = reinterpret_cast<LPCWSTR>(cp);
@ -37,123 +35,12 @@ static km_kbp_cp* CloneKMKBPCPFromWSTR(LPWSTR buf) {
return clone;
}
void LoadKeyboardOptions(LPINTKEYBOARDINFO kp)
{ // I3594
IntLoadKeyboardOptions(REGSZ_KeyboardOptions, kp);
}
void LoadSharedKeyboardOptions(LPINTKEYBOARDINFO kp)
void SaveKeyboardOptionCoretoRegistry(LPINTKEYBOARDINFO kp, LPCWSTR key, LPWSTR value)
{
if(!DebugAssert(!Globals::get_CoreIntegration(), "LoadSharedKeyboardOptions: Error called in core integration mode")) {
return;
}
// Called when another thread changes keyboard options and we are sharing keyboard settings
assert(kp != NULL);
assert(kp->Keyboard != NULL);
if(kp->KeyboardOptions != NULL) FreeKeyboardOptions(kp);
IntLoadKeyboardOptions(REGSZ_SharedKeyboardOptions, kp);
IntSaveKeyboardOptionCoretoRegistry(REGSZ_KeyboardOptions, kp, key, value);
}
void FreeKeyboardOptions(LPINTKEYBOARDINFO kp)
{
// This is a cleanup routine; we don't want to precondition all calls to it
// so we do not assert
if (kp == NULL || kp->Keyboard == NULL || kp->KeyboardOptions == NULL)
return;
for(DWORD i = 0; i < kp->Keyboard->cxStoreArray; i++)
if(kp->KeyboardOptions[i].Value)
{
kp->Keyboard->dpStoreArray[i].dpString = kp->KeyboardOptions[i].OriginalStore;
delete kp->KeyboardOptions[i].Value;
}
delete kp->KeyboardOptions;
kp->KeyboardOptions = NULL;
}
void SetKeyboardOption(LPINTKEYBOARDINFO kp, int nStoreToSet, int nStoreToRead)
{
if (!DebugAssert(!Globals::get_CoreIntegration(), "SetKeyboardOption: Error called in core integration mode")) {
return;
}
assert(kp != NULL);
assert(kp->Keyboard != NULL);
assert(kp->KeyboardOptions != NULL);
assert(nStoreToSet >= 0);
assert(nStoreToSet < (int) kp->Keyboard->cxStoreArray);
assert(nStoreToRead >= 0);
assert(nStoreToRead < (int) kp->Keyboard->cxStoreArray);
LPSTORE sp = &kp->Keyboard->dpStoreArray[nStoreToRead];
if(kp->KeyboardOptions[nStoreToSet].Value)
{
delete kp->KeyboardOptions[nStoreToSet].Value;
}
else
{
kp->KeyboardOptions[nStoreToSet].OriginalStore = kp->Keyboard->dpStoreArray[nStoreToSet].dpString;
}
kp->KeyboardOptions[nStoreToSet].Value = new WCHAR[wcslen(sp->dpString)+1];
wcscpy_s(kp->KeyboardOptions[nStoreToSet].Value, wcslen(sp->dpString)+1, sp->dpString);
kp->Keyboard->dpStoreArray[nStoreToSet].dpString = kp->KeyboardOptions[nStoreToSet].Value;
}
void ResetKeyboardOption(LPINTKEYBOARDINFO kp, int nStoreToReset)
{
if (!DebugAssert(!Globals::get_CoreIntegration(), "ResetKeyboardOption: Error called in core integration mode")) {
return;
}
assert(kp != NULL);
assert(kp->Keyboard != NULL);
assert(kp->KeyboardOptions != NULL);
assert(nStoreToReset >= 0);
assert(nStoreToReset < (int) kp->Keyboard->cxStoreArray);
if(kp->KeyboardOptions[nStoreToReset].Value)
{
kp->Keyboard->dpStoreArray[nStoreToReset].dpString = kp->KeyboardOptions[nStoreToReset].OriginalStore;
delete kp->KeyboardOptions[nStoreToReset].Value;
kp->KeyboardOptions[nStoreToReset].Value = NULL;
if(kp->Keyboard->dpStoreArray[nStoreToReset].dpName == NULL) return;
RegistryReadOnly r(HKEY_CURRENT_USER);
if(r.OpenKeyReadOnly(REGSZ_KeymanActiveKeyboards) && r.OpenKeyReadOnly(kp->Name) && r.OpenKeyReadOnly(REGSZ_KeyboardOptions))
{
if(r.ValueExists(kp->Keyboard->dpStoreArray[nStoreToReset].dpName))
{
WCHAR val[256];
if(!r.ReadString(kp->Keyboard->dpStoreArray[nStoreToReset].dpName, val, sizeof(val) / sizeof(val[0]))) return;
if(!val[0]) return;
val[255] = 0;
kp->KeyboardOptions[nStoreToReset].Value = new WCHAR[wcslen(val)+1];
wcscpy_s(kp->KeyboardOptions[nStoreToReset].Value, wcslen(val)+1, val);
kp->KeyboardOptions[nStoreToReset].OriginalStore = kp->Keyboard->dpStoreArray[nStoreToReset].dpString;
kp->Keyboard->dpStoreArray[nStoreToReset].dpString = kp->KeyboardOptions[nStoreToReset].Value;
}
}
}
}
void SaveKeyboardOption(LPINTKEYBOARDINFO kp, int nStoreToSave)
{
if (!DebugAssert(!Globals::get_CoreIntegration(), "SaveKeyboardOption: Error called in core integration mode")) {
return;
}
IntSaveKeyboardOption(REGSZ_KeyboardOptions, kp, nStoreToSave);
}
void SaveKeyboardOptionREGCore(LPINTKEYBOARDINFO kp, LPCWSTR key, LPWSTR value)
{
IntSaveKeyboardOptionREGCore(REGSZ_KeyboardOptions, kp, key, value);
}
void IntSaveKeyboardOptionREGCore(LPCSTR REGKey, LPINTKEYBOARDINFO kp, LPCWSTR key, LPWSTR value)
void IntSaveKeyboardOptionCoretoRegistry(LPCSTR REGKey, LPINTKEYBOARDINFO kp, LPCWSTR key, LPWSTR value)
{
assert(REGKey != NULL);
assert(kp != NULL);
@ -167,73 +54,13 @@ void IntSaveKeyboardOptionREGCore(LPCSTR REGKey, LPINTKEYBOARDINFO kp, LPCWSTR k
}
}
BOOL IntLoadKeyboardOptions(LPCSTR key, LPINTKEYBOARDINFO kp)
void LoadKeyboardOptionsRegistrytoCore(LPINTKEYBOARDINFO kp, km_kbp_state* const state)
{
assert(key != NULL);
assert(kp != NULL);
assert(kp->Keyboard != NULL);
assert(kp->KeyboardOptions == NULL);
kp->KeyboardOptions = new INTKEYBOARDOPTIONS[kp->Keyboard->cxStoreArray];
memset(kp->KeyboardOptions, 0, sizeof(INTKEYBOARDOPTIONS) * kp->Keyboard->cxStoreArray);
RegistryReadOnly r(HKEY_CURRENT_USER);
if(r.OpenKeyReadOnly(REGSZ_KeymanActiveKeyboards) && r.OpenKeyReadOnly(kp->Name) && r.OpenKeyReadOnly(key))
{
WCHAR buf[256];
int n = 0;
while(r.GetValueNames(buf, sizeof(buf) / sizeof(buf[0]), n))
{
buf[255] = 0;
WCHAR val[256];
if(r.ReadString(buf, val, sizeof(val) / sizeof(val[0])) && val[0])
{
val[255] = 0;
for(DWORD i = 0; i < kp->Keyboard->cxStoreArray; i++)
{
if(kp->Keyboard->dpStoreArray[i].dpName != NULL && _wcsicmp(kp->Keyboard->dpStoreArray[i].dpName, buf) == 0)
{
kp->KeyboardOptions[i].Value = new WCHAR[wcslen(val)+1];
wcscpy_s(kp->KeyboardOptions[i].Value, wcslen(val)+1, val);
kp->KeyboardOptions[i].OriginalStore = kp->Keyboard->dpStoreArray[i].dpString;
kp->Keyboard->dpStoreArray[i].dpString = kp->KeyboardOptions[i].Value;
break;
}
}
}
n++;
}
return TRUE;
}
return FALSE;
SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadKeyboardOptionsRegistrytoCore: Enter");
IntLoadKeyboardOptionsRegistrytoCore(REGSZ_KeyboardOptions, kp, state);
}
void IntSaveKeyboardOption(LPCSTR key, LPINTKEYBOARDINFO kp, int nStoreToSave)
{
assert(key != NULL);
assert(kp != NULL);
assert(kp->Keyboard != NULL);
assert(kp->KeyboardOptions != NULL);
assert(nStoreToSave >= 0);
assert(nStoreToSave < (int) kp->Keyboard->cxStoreArray);
if(kp->Keyboard->dpStoreArray[nStoreToSave].dpName == NULL) return;
RegistryFullAccess r(HKEY_CURRENT_USER);
if(r.OpenKey(REGSZ_KeymanActiveKeyboards, TRUE) && r.OpenKey(kp->Name, TRUE) && r.OpenKey(key, TRUE))
{
r.WriteString(kp->Keyboard->dpStoreArray[nStoreToSave].dpName, kp->Keyboard->dpStoreArray[nStoreToSave].dpString);
}
}
void LoadKeyboardOptionsREGCore(LPINTKEYBOARDINFO kp, km_kbp_state* const state)
{
SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadKeyboardOptionsREGCore: Enter");
IntLoadKeyboardOptionsCore(REGSZ_KeyboardOptions, kp, state);
}
BOOL IntLoadKeyboardOptionsCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_kbp_state* const state)
BOOL IntLoadKeyboardOptionsRegistrytoCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_kbp_state* const state)
{
assert(key != NULL);
assert(kp != NULL);
@ -243,7 +70,7 @@ BOOL IntLoadKeyboardOptionsCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_kbp_state*
km_kbp_status err_status = km_kbp_keyboard_get_attrs(kp->lpCoreKeyboard, &keyboardAttrs);
if (err_status != KM_KBP_STATUS_OK) {
SendDebugMessageFormat(
0, sdmKeyboard, 0, "LoadKeyboardOptionsREGCore: km_kbp_keyboard_get_attrs failed with error status [%d]", err_status);
0, sdmKeyboard, 0, "LoadKeyboardOptionsRegistrytoCore: km_kbp_keyboard_get_attrs failed with error status [%d]", err_status);
return FALSE;
}
@ -275,7 +102,7 @@ BOOL IntLoadKeyboardOptionsCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_kbp_state*
err_status = km_kbp_state_options_update(state, keyboardOpts);
if (err_status != KM_KBP_STATUS_OK) {
SendDebugMessageFormat(
0, sdmKeyboard, 0, "LoadKeyboardOptionsREGCore: km_kbp_state_options_update failed with error status [%d]", err_status);
0, sdmKeyboard, 0, "LoadKeyboardOptionsRegistrytoCore: km_kbp_state_options_update failed with error status [%d]", err_status);
}
for (int i = 0; i < n; i++) {
delete[] keyboardOpts[i].value;
@ -283,89 +110,3 @@ BOOL IntLoadKeyboardOptionsCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_kbp_state*
delete[] keyboardOpts;
return TRUE;
}
BOOL
UpdateKeyboardOptionsCore(
km_kbp_state* const lpCoreKeyboardState,
km_kbp_option_item *lpCoreKeyboardOptions) {
int listSize = (int)km_kbp_options_list_size(lpCoreKeyboardOptions);
// Create a option list based on this size look up each key and store the return value in it.
// then at the end return this options list.
BOOL changed = FALSE;
km_kbp_cp const* retValue = nullptr;
for (int i = 0; i < listSize; i++) {
km_kbp_status err_status = km_kbp_state_option_lookup(lpCoreKeyboardState, lpCoreKeyboardOptions[i].scope, lpCoreKeyboardOptions[i].key,
&retValue);
if (err_status != KM_KBP_STATUS_OK) {
SendDebugMessageFormat(
0, sdmKeyboard, 0, "UpdateKeyboardOptionsCore: km_kbp_state_option_lookup failed with error status [%d]", err_status);
continue;
}
// compare to see if changed
if (wcscmp(reinterpret_cast<LPCWSTR>(retValue), reinterpret_cast<LPCWSTR>(lpCoreKeyboardOptions[i].value)) != 0) {
delete lpCoreKeyboardOptions[i].value;
lpCoreKeyboardOptions[i].value = CloneKMKBPCP(retValue);
changed = TRUE;
}
}
return changed;
}
km_kbp_option_item*
SaveKeyboardOptionsCore(LPINTKEYBOARDINFO kp) {
// Get the list of default options to determine size of list
const km_kbp_keyboard_attrs* keyboardAttrs;
km_kbp_status err_status = km_kbp_keyboard_get_attrs(kp->lpCoreKeyboard, &keyboardAttrs);
if (err_status != KM_KBP_STATUS_OK) {
SendDebugMessageFormat(
0, sdmKeyboard, 0, "SaveKeyboardOptionsCore: km_kbp_keyboard_get_attrs failed with error status [%d]", err_status);
return nullptr;
}
int listSize = (int)km_kbp_options_list_size(keyboardAttrs->default_options);
km_kbp_option_item* savedKeyboardOpts = new km_kbp_option_item[listSize + 1];
km_kbp_cp const* retValue = nullptr;
km_kbp_option_item const* kbDefaultOpts = keyboardAttrs->default_options;
for (int i = 0; i < listSize; i++, ++kbDefaultOpts) {
if (kbDefaultOpts->scope != KM_KBP_OPT_KEYBOARD)
continue;
err_status =
km_kbp_state_option_lookup(kp->lpCoreKeyboardState, KM_KBP_OPT_KEYBOARD, kbDefaultOpts->key, &retValue);
if (err_status != KM_KBP_STATUS_OK) {
SendDebugMessageFormat(
0, sdmKeyboard, 0, "SaveKeyboardOptionsCore: km_kbp_state_option_lookup failed with error status [%d]", err_status);
continue;
}
savedKeyboardOpts[i].key = CloneKMKBPCP(kbDefaultOpts->key);
savedKeyboardOpts[i].value = CloneKMKBPCP(retValue);
savedKeyboardOpts[i].scope = KM_KBP_OPT_KEYBOARD;
}
savedKeyboardOpts[listSize] = KM_KBP_OPTIONS_END;
return savedKeyboardOpts;
}
BOOL
RestoreKeyboardOptionsCore(
km_kbp_state* const lpCoreKeyboardState,
km_kbp_option_item* lpCoreKeyboardOptions) {
km_kbp_status err_status = km_kbp_state_options_update(lpCoreKeyboardState, lpCoreKeyboardOptions);
if (err_status != KM_KBP_STATUS_OK) {
SendDebugMessageFormat(
0, sdmKeyboard, 0, "RestoreKeyboardOptionsCore: km_kbp_state_options_update failed with error status [%d]", err_status);
return FALSE;
}
return TRUE;
}
void
DisposeKeyboardOptionsCore(km_kbp_option_item** lpCoreKeyboardOptions) {
size_t listSize = km_kbp_options_list_size(*lpCoreKeyboardOptions);
for (int i = 0; i < (int)listSize; i++) {
delete[] (*lpCoreKeyboardOptions)[i].key;
delete[] (*lpCoreKeyboardOptions)[i].value;
}
delete[] *lpCoreKeyboardOptions;
*lpCoreKeyboardOptions = NULL;
}

View file

@ -16,44 +16,6 @@
History: 25 May 2010 - mcdurdin - I1632 - Keyboard Options
*/
void LoadKeyboardOptions(LPINTKEYBOARDINFO kp);
void FreeKeyboardOptions(LPINTKEYBOARDINFO kp);
void SetKeyboardOption(LPINTKEYBOARDINFO kp, int nStoreToSet, int nStoreToRead);
void ResetKeyboardOption(LPINTKEYBOARDINFO kp, int nStoreToReset);
void SaveKeyboardOption(LPINTKEYBOARDINFO kp, int nStoreToSave);
void LoadSharedKeyboardOptions(LPINTKEYBOARDINFO kp);
/**
* Updates the supplied Keyboard processor options list from the keyboard processor pointed
* to by the state pointer.
*
* @param lpCoreKeyboardState The core keyboardprocessor state which has the source options
* @param[in,out] lpCoreKeyboardOptions The core keyboard options to be updated
* @return BOOL True if one or more options were updated
*/
BOOL UpdateKeyboardOptionsCore(km_kbp_state* const lpCoreKeyboardState, km_kbp_option_item *lpCoreKeyboardOptions);
/**
* Returns a copy of the core keyboard processors current keyboard options
* The caller is responsible for freeing the returned km_kbp_option_item's list.
*
* @param kp A pointer to the keyboard info object that contains the
* keyboardprocessor state and keyboard for the source options list.
*
* @return km_kbp_option_item* The copy of the options list or NULL if copy failed
*/
km_kbp_option_item* SaveKeyboardOptionsCore(LPINTKEYBOARDINFO kp);
/**
* Restore the core keyboard processor options to the supplied keyboard
* list of `km_kbp_option_item`s
*
* @param lpCoreKeyboardState The state pointer for the keyboard processor
* @param lpCoreKeyboardOptions The list of `km_kbp_option_item`s to restore
*
* return BOOL TRUE when the call to update keyboard processor was successful
*/
BOOL RestoreKeyboardOptionsCore(km_kbp_state* const lpCoreKeyboardState, km_kbp_option_item* lpCoreKeyboardOptions);
/* Common core integration functions */
/**
@ -62,7 +24,7 @@ BOOL RestoreKeyboardOptionsCore(km_kbp_state* const lpCoreKeyboardState, km_kbp_
* @param kp keyboard info object with options to be updated
* @param state core keyboard state used to update keyboard options
*/
void LoadKeyboardOptionsREGCore(LPINTKEYBOARDINFO kp, km_kbp_state* state);
void LoadKeyboardOptionsRegistrytoCore(LPINTKEYBOARDINFO kp, km_kbp_state* state);
/**
* Saves the keyboard option to the windows registry
@ -71,12 +33,6 @@ void LoadKeyboardOptionsREGCore(LPINTKEYBOARDINFO kp, km_kbp_state* state);
* @param key keyboard key to save
* @param value keyboard option value to save
*/
void SaveKeyboardOptionREGCore(LPINTKEYBOARDINFO kp, LPCWSTR key, LPWSTR value);
void SaveKeyboardOptionCoretoRegistry(LPINTKEYBOARDINFO kp, LPCWSTR key, LPWSTR value);
/**
* Free the allocated resources belonging to a key_kbp_option_items object
* that was created on the heap most likely using SaveKeyboardOptionREGCore
*
* @param lpCoreKeyboardOptions keyboard options items to be freed
*/
void DisposeKeyboardOptionsCore(km_kbp_option_item** lpCoreKeyboardOptions);

View file

@ -59,7 +59,7 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LibraryPath>$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\src;$(ProjectDir)..\..\..\..\core\build\rust\x86\$(Configuration);$(LibraryPath)</LibraryPath>
<IncludePath>$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\include;$(IncludePath)</IncludePath>
<IncludePath>$(ProjectDir)..\..\..\..\common\include;$(ProjectDir)..\..\..\..\core\include;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Midl>
@ -186,12 +186,6 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="$(KEYMAN_ROOT)\common\windows\cpp\src\xstring.cpp" />
<ClCompile Include="addins.cpp">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<ClCompile Include="appint\aiTIP.cpp">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -403,4 +397,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View file

@ -15,9 +15,6 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="addins.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="appint\aiTIP.cpp">
<Filter>Source Files</Filter>
</ClCompile>

View file

@ -492,7 +492,6 @@ extern "C" BOOL _declspec(dllexport) WINAPI Keyman_Exit(void)
*Globals::Keyman_Shutdown() = TRUE;
ReleaseKeyboards(TRUE);
Addin_Release();
if(!Globals::get_Keyman_Initialised())
{
@ -529,138 +528,6 @@ extern "C" BOOL _declspec(dllexport) WINAPI Keyman_RestartEngine()
return TRUE;
}
/*******************************************************************************************/
/* */
/* Keyman Keyboard Override Functions */
/* */
/*******************************************************************************************/
extern "C" BOOL _declspec(dllexport) WINAPI Keyman_StopForcingKeyboard();
void RefreshPreservedKeys(BOOL Activating);
extern "C" BOOL _declspec(dllexport) WINAPI Keyman_ForceKeyboard(PCSTR FileName)
{
SendDebugMessageFormat(0,sdmGlobal,0,"Keyman_ForceKeyboard: ENTER %s", FileName);
Keyman_StopForcingKeyboard(); // 7.0.219.0
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
strncpy(_td->ForceFileName, FileName, MAX_PATH - 1);
_td->ForceFileName[MAX_PATH-1] = 0;
if(_td->lpActiveKeyboard)
{
DeactivateDLLs(_td->lpActiveKeyboard);
}
_td->lpActiveKeyboard = new INTKEYBOARDINFO;
memset(_td->lpActiveKeyboard, 0, sizeof(INTKEYBOARDINFO)); // I2437 - Crash unloading keyboard due to keyboard options not init
/*_td->lpActiveKeyboard->KeymanID = 0;
_td->lpActiveKeyboard->nIMDLLs = 0;
_td->lpActiveKeyboard->IMDLLs = NULL;
_td->lpActiveKeyboard->KeyboardOptions = NULL;*/
_splitpath_s(FileName, NULL, 0, NULL, 0, _td->lpActiveKeyboard->Name, sizeof(_td->lpActiveKeyboard->Name), NULL, 0);
// TODO: 5442 - remove if/ else as there will no longer be the old LoadKeyboard option
if (Globals::get_CoreIntegration()) {
PWCHAR keyboardPath = strtowstr(_td->ForceFileName);
km_kbp_status err_status = km_kbp_keyboard_load(keyboardPath, &_td->lpActiveKeyboard->lpCoreKeyboard);
if (err_status != KM_KBP_STATUS_OK) {
SendDebugMessageFormat(0, sdmGlobal, 0, "Keyman_ForceKeyboard Core: km_kbp_keyboard_load failed for %ls with error status [%d]", keyboardPath, err_status); // TODO: 5442 - remove word Core
delete keyboardPath;
return FALSE;
}
delete keyboardPath;
SendDebugMessageFormat(0, sdmGlobal, 0, "Keyman_ForceKeyboard Core: %s OK", FileName); // TODO: 5442 - remove word Core
km_kbp_option_item *core_environment = nullptr;
if(!SetupCoreEnvironment(&core_environment)) {
SendDebugMessageFormat(0, sdmLoad, 0, "Keyman_ForceKeyboard Core: Unable to set environment options for keyboard %s", FileName); // TODO: 5442 - remove word Core
return FALSE;
}
err_status =
km_kbp_state_create(_td->lpActiveKeyboard->lpCoreKeyboard, core_environment, &_td->lpActiveKeyboard->lpCoreKeyboardState);
DeleteCoreEnvironment(core_environment);
if (err_status != KM_KBP_STATUS_OK) {
SendDebugMessageFormat(
0, sdmGlobal, 0, "Keyman_ForceKeyboard Core: km_kbp_state_create failed with error status [%d]", err_status);
// Dispose of the keyboard to leave us in a consitent state
ReleaseKeyboardMemoryCore(&_td->lpActiveKeyboard->lpCoreKeyboard);
return FALSE;
}
ResetCapsLock();
err_status = km_kbp_keyboard_get_imx_list(_td->lpActiveKeyboard->lpCoreKeyboard, &_td->lpActiveKeyboard->lpIMXList);
if (err_status != KM_KBP_STATUS_OK) {
SendDebugMessageFormat(0, sdmLoad, 0, "Keyman_ForceKeyboard Core: km_kbp_keyboard_get_imx_list failed with error status [%d]", err_status);
// Dispose of the keyboard to leave us in a consistent state
ReleaseKeyboardMemoryCore(&_td->lpActiveKeyboard->lpCoreKeyboard);
return FALSE;
}
LoadDLLsCore(_td->lpActiveKeyboard);
ActivateDLLs(_td->lpActiveKeyboard);
LoadKeyboardOptionsREGCore(_td->lpActiveKeyboard, _td->lpActiveKeyboard->lpCoreKeyboardState);
RefreshPreservedKeys(TRUE);
return TRUE;
} else {
if (LoadKeyboard(_td->ForceFileName, &_td->lpActiveKeyboard->Keyboard)) {
SendDebugMessageFormat(0, sdmGlobal, 0, "Keyman_ForceKeyboard: %s OK", FileName);
ResetCapsLock();
LoadDLLs(_td->lpActiveKeyboard);
ActivateDLLs(_td->lpActiveKeyboard);
LoadKeyboardOptions(_td->lpActiveKeyboard); // I2437 - Crash unloading keyboard due to keyboard options not set
RefreshPreservedKeys(TRUE);
return TRUE;
}
}
SendDebugMessageFormat(0,sdmGlobal,0,"Keyman_ForceKeyboard: %s FAIL", FileName);
delete _td->lpActiveKeyboard;
_td->lpActiveKeyboard = NULL;
_td->ForceFileName[0] = 0;
return FALSE;
}
extern "C" BOOL _declspec(dllexport) WINAPI Keyman_StopForcingKeyboard()
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td)
{
SetLastError(ERROR_KEYMAN_THREAD_DATA_NOT_READY); // I3173 // I3525
return FALSE;
}
if(!_td->lpActiveKeyboard)
{
SetLastError(ERROR_KEYMAN_KEYBOARD_NOT_ACTIVE); // I3173 // I3525
return FALSE;
}
SendDebugMessageFormat(0,sdmGlobal,0,"Keyman_StopForcingKeyboard");
if(_td->ForceFileName[0])
{
SendDebugMessageFormat(0,sdmGlobal,0,"Keyman_StopForcingKeyboard: Stopping forcing");
if(!DeactivateDLLs(_td->lpActiveKeyboard)) return FALSE; // I3173 // I3525
if(!UnloadDLLs(_td->lpActiveKeyboard)) return FALSE; // I3173 // I3525
_td->ForceFileName[0] = 0;
FreeKeyboardOptions(_td->lpActiveKeyboard);
ReleaseKeyboardMemory(_td->lpActiveKeyboard->Keyboard);
ReleaseStateMemoryCore(&_td->lpActiveKeyboard->lpCoreKeyboardState);
ReleaseKeyboardMemoryCore(&_td->lpActiveKeyboard->lpCoreKeyboard);
RefreshPreservedKeys(FALSE);
delete _td->lpActiveKeyboard;
_td->lpActiveKeyboard = NULL;
}
return TRUE;
}
//---------------------------------------------------------------------------------------------------------
//
// Utility guff functions
@ -936,8 +803,6 @@ void RefreshKeyboards(BOOL Initialising)
// Can happen when multiple top-level windows for one process
Addin_Refresh();
SendDebugMessageFormat(0,sdmGlobal,0,"---ENTER RefreshKeyboards---");
//FInRefreshKeyboards = TRUE;
@ -1006,8 +871,6 @@ void RefreshKeyboards(BOOL Initialising)
RefreshKeyboardProfiles(kp, FALSE); // Read standard profiles
RefreshKeyboardProfiles(kp, TRUE); // Read transient profiles
kp->Keyboard = NULL;
SendDebugMessageFormat(0,sdmGlobal,0,"RefreshKeyboards: Added keyboard %s, %d",
kp->Name, kp->KeymanID);
i++;
@ -1035,13 +898,15 @@ void ReleaseKeyboards(BOOL Lock)
if(!_td || !_td->lpKeyboards) return;
if(Lock) if(_td->lpActiveKeyboard && !_td->ForceFileName[0]) DeactivateDLLs(_td->lpActiveKeyboard);
if(Lock) {
if(_td->lpActiveKeyboard) {
DeactivateDLLs(_td->lpActiveKeyboard);
}
}
for(int i = 0; i < _td->nKeyboards; i++)
{
if(Lock) UnloadDLLs(&_td->lpKeyboards[i]);
FreeKeyboardOptions(&_td->lpKeyboards[i]);
ReleaseKeyboardMemory(_td->lpKeyboards[i].Keyboard);
ReleaseStateMemoryCore(&_td->lpKeyboards[i].lpCoreKeyboardState);
ReleaseKeyboardMemoryCore(&_td->lpKeyboards[i].lpCoreKeyboard);
if(_td->lpKeyboards[i].Profiles) delete _td->lpKeyboards[i].Profiles; // I3581
@ -1051,7 +916,6 @@ void ReleaseKeyboards(BOOL Lock)
delete _td->lpKeyboards;
_td->lpKeyboards = NULL;
if(!_td->ForceFileName[0]) _td->lpActiveKeyboard = NULL;
}
/**

View file

@ -4,8 +4,6 @@ EXPORTS
Keyman_GetInitialised
Keyman_Initialise
Keyman_Exit
Keyman_ForceKeyboard
Keyman_StopForcingKeyboard
Keyman_GetLastActiveWindow
Keyman_GetLastFocusWindow
KMSetOutput

View file

@ -176,12 +176,6 @@
<ItemGroup>
<ClCompile Include="..\..\global\cpp\kmtip_guids.cpp" />
<ClCompile Include="$(KEYMAN_ROOT)\common\windows\cpp\src\xstring.cpp" />
<ClCompile Include="addins.cpp">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<ClCompile Include="appint\aiTIP.cpp">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -362,7 +356,6 @@
<ItemGroup>
<ClInclude Include="keymanengine.h" />
<ClInclude Include="..\..\..\include\kmtip_guids.h" />
<ClInclude Include="addins.h" />
<ClInclude Include="appint\aiTIP.h" />
<ClInclude Include="appint\aiWin2000Unicode.h" />
<ClInclude Include="appint\appint.h" />

View file

@ -15,9 +15,6 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="addins.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="appint\aiTIP.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -162,9 +159,6 @@
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="addins.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="appint\aiTIP.h">
<Filter>Header Files</Filter>
</ClInclude>

View file

@ -81,11 +81,9 @@ typedef struct tagINTKEYBOARDINFO
DWORD __filler_Hotkey;
DWORD __filler; // makes same as KEYBOARDINFO // I4462
char Name[256];
LPKEYBOARD Keyboard;
DWORD nIMDLLs;
LPIMDLL IMDLLs;
int __filler2; // makes same as KEYBOARDINFO
LPINTKEYBOARDOPTIONS KeyboardOptions;
int nProfiles;
LPINTKEYBOARDPROFILE Profiles;
km_kbp_keyboard* lpCoreKeyboard;
@ -105,17 +103,11 @@ typedef struct tagKMSTATE
{
BOOL NoMatches;
MSG msg;
// TODO: 5442 will remove these once windows core is deprecated
BOOL StopOutput;
int LoopTimes;
// TODO: 5442
WORD vkey; // I934
WCHAR charCode; // I4582
BOOL windowunicode; // I4287
BOOL isDown;
LPKEYBOARD lpkb;
km_kbp_keyboard* lpCoreKb; // future use with IMDLL
LPGROUP startgroup; // TODO: 5442 will remove this once windows core is deprecated
} KMSTATE;
// I3616
@ -129,7 +121,6 @@ LRESULT CALLBACK kmnLowLevelKeyboardProc( // I4124
_In_ LPARAM lParam
);
BOOL ReleaseKeyboardMemory(LPKEYBOARD kbd);
BOOL ReleaseStateMemoryCore(km_kbp_state** state);
BOOL ReleaseKeyboardMemoryCore(km_kbp_keyboard** kbd);
@ -230,8 +221,6 @@ DWORD HKLToKeyboardID(HKL hkl);
WORD HKLToLanguageID(HKL hkl);
WORD HKLToLayoutNumber(HKL hkl);
WORD HKLToLayoutID(HKL hkl);
DWORD EthnologueCodeToKeymanID(DWORD EthCode);
DWORD EthnologueStringCodeToDWord(PWSTR EthCode);
PWSTR GetSystemStore(LPKEYBOARD kb, DWORD SystemID);
@ -254,7 +243,6 @@ void keybd_shift(LPINPUT pInputs, int* n, BOOL isReset, LPBYTE const kbd);
#include "keystate.h"
#include "calldll.h"
#include "addins.h"
#include "keymancontrol.h"
#include "keyboardoptions.h"
#include "kmprocessactions.h"

View file

@ -248,19 +248,6 @@ LRESULT _kmnGetMessageProc(int nCode, WPARAM wParam, LPARAM lParam)
return CallNextHookEx(Globals::get_hhookGetMessage(), nCode, wParam, lParam);
}
/*
Handle WM_UNICHAR messages for RichEdit control -- should we test RichEdit version?
*/
if(mp->message == WM_UNICHAR && Addin_ShouldProcessUnichar(mp->hwnd))
{
if(Addin_ProcessUnichar(mp->hwnd, (DWORD) mp->wParam))
{
mp->message = 0;
return CallNextHookEx(Globals::get_hhookGetMessage(), nCode, wParam, lParam);
}
}
/*
Handle wm_keyman_control_internal messages
*/
@ -271,7 +258,6 @@ LRESULT _kmnGetMessageProc(int nCode, WPARAM wParam, LPARAM lParam)
if(_td->lpActiveKeyboard)
{
_td->state.lpkb = _td->lpActiveKeyboard->Keyboard;
_td->state.lpCoreKb = _td->lpActiveKeyboard->lpCoreKeyboard;
}
// I4412
@ -297,20 +283,17 @@ LRESULT _kmnGetMessageProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if(_td->app)
{
BOOL isUsingCoreProcessor = Globals::get_CoreIntegration();
if (isUsingCoreProcessor) {
// Call the core keyboard processor to process the queued actions
if (!_td->lpActiveKeyboard) {
return CallNextHookEx(Globals::get_hhookGetMessage(), nCode, wParam, lParam);
}
if (KM_KBP_STATUS_OK != km_kbp_process_queued_actions(_td->lpActiveKeyboard->lpCoreKeyboardState)) {
SendDebugMessageFormat(0, sdmGlobal, 0, "_kmnGetMessageProc wm_keymanim_close process event fail");
return CallNextHookEx(Globals::get_hhookGetMessage(), nCode, wParam, lParam);
}
BOOL emitKeyStroke;
ProcessActions(&emitKeyStroke);
// Call the core keyboard processor to process the queued actions
if (!_td->lpActiveKeyboard) {
return CallNextHookEx(Globals::get_hhookGetMessage(), nCode, wParam, lParam);
}
if (KM_KBP_STATUS_OK != km_kbp_process_queued_actions(_td->lpActiveKeyboard->lpCoreKeyboardState)) {
SendDebugMessageFormat(0, sdmGlobal, 0, "_kmnGetMessageProc wm_keymanim_close process event fail");
return CallNextHookEx(Globals::get_hhookGetMessage(), nCode, wParam, lParam);
}
BOOL emitKeyStroke;
ProcessActions(&emitKeyStroke);
_td->app->SetCurrentShiftState(Globals::get_ShiftState());
_td->app->SendActions();
}
@ -372,7 +355,6 @@ void ProcessWMKeyman(HWND hwnd, WPARAM wParam, LPARAM lParam)
hwnd = GetFocus();
if(_td->lpActiveKeyboard) {
_td->state.lpkb = _td->lpActiveKeyboard->Keyboard;
_td->state.lpCoreKb = _td->lpActiveKeyboard->lpCoreKeyboard;
}
@ -384,7 +366,6 @@ void ProcessWMKeyman(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
if(_td->app) _td->app->ResetQueue();
GetCapsAndNumlockState();
Addin_FocusChanged(hwnd);
UpdateActiveWindows();
}
}

View file

@ -119,7 +119,6 @@ Process_Event_Core(PKEYMAN64THREADDATA _td) {
return TRUE;
}
/*
* BOOL ProcessHook();
*
@ -138,10 +137,7 @@ BOOL ProcessHook()
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
LPGROUP gp = _td->state.startgroup;
fOutputKeystroke = FALSE; // TODO: 5442 no longer needs to be global once we use core processor
BOOL isUsingCoreProcessor = Globals::get_CoreIntegration();
//
// If we are running in the debugger, don't do a second run through
//
@ -160,44 +156,28 @@ BOOL ProcessHook()
Debug_VirtualKey(_td->state.vkey), getcontext_debug());
}
AIDEBUGKEYINFO keyinfo;
keyinfo.shiftFlags = Globals::get_ShiftState();
keyinfo.VirtualKey = _td->state.vkey;
keyinfo.Character = _td->state.charCode;
keyinfo.DeadKeyCharacter = 0; // I4582
keyinfo.IsUp = !_td->state.isDown;
if(_td->app->IsUnicode())
_td->app->QueueDebugInformation(QID_BEGIN_UNICODE, NULL, NULL, NULL, NULL, (DWORD_PTR) &keyinfo);
else
_td->app->QueueDebugInformation(QID_BEGIN_ANSI, NULL, NULL, NULL, NULL, (DWORD_PTR) &keyinfo);
}
if (isUsingCoreProcessor) { // TODO: 5442 Note: Nested if will be reduced once using core only
// For applications not using the TSF kmtip calls this function twice for each keystroke,
// first to determine if we are doing processing work (TIPFUpdateable == FALSE),
// if we say yes it will call a second time to actually do the work.
// We call the core process event only once and use the core's queued actions
// on the second pass.
// For the TSF in most cases kmtip (except OnPreservedKey) will not call the non-updateable test parse.
// Therfore the core process event will need to be called before processing the actions.
// For applications not using the TSF kmtip calls this function twice for each keystroke,
// first to determine if we are doing processing work (TIPFUpdateable == FALSE),
// if we say yes it will call a second time to actually do the work.
// We call the core process event only once and use the core's queued actions
// on the second pass.
// For the TSF in most cases kmtip (except OnPreservedKey) will not call the non-updateable test parse.
// Therfore the core process event will need to be called before processing the actions.
// CoreProcessEventRun would be a sufficient test however testing TIPFUpdateable defines
// the status of the keystroke processing more precisely.
if (!_td->TIPFUpdateable || !_td->CoreProcessEventRun) {
if (!Process_Event_Core(_td)) {
return FALSE;
}
// CoreProcessEventRun would be a sufficient test however testing TIPFUpdateable defines
// the status of the keystroke processing more precisely.
if (!_td->TIPFUpdateable || !_td->CoreProcessEventRun) {
if (!Process_Event_Core(_td)) {
return FALSE;
}
if (!_td->TIPFUpdateable) {
ProcessActionsNonUpdatableParse(&fOutputKeystroke);
} else {
ProcessActions(&fOutputKeystroke);
}
}
else {
ProcessGroup(gp); // TODO: 5442 remove
if (!_td->TIPFUpdateable) {
ProcessActionsNonUpdatableParse(&fOutputKeystroke);
} else {
ProcessActions(&fOutputKeystroke);
}
if (fOutputKeystroke && !_td->app->IsQueueEmpty()) {
@ -256,671 +236,9 @@ BOOL ProcessHook()
// PWSTR contextBuf = _td->app->ContextBufMax(MAXCONTEXT);
// SendDebugMessageFormat(0, sdmAIDefault, 0, "Kmprocess::ProcessHook After cxt=%s", Debug_UnicodeString(contextBuf, 1));
_td->app->QueueDebugInformation(QID_END, NULL, NULL, NULL, NULL, 0);
return !fOutputKeystroke;
}
/*
* PRIVATE BOOL ProcessGroup(LPGROUP gp);
*
* Parameters: gp Pointer to group to process inside
*
* Returns: TRUE if messages are to be sent,
* and FALSE if no messages are to be sent.
*
* Called by: ProcessHook, recursive inside groups
*
* ProcessKey is where the keystroke conversion and output takes place. This routine
* has a lot of crucial code in it!
*/
BOOL ProcessGroup(LPGROUP gp)
{
if (!DebugAssert(!Globals::get_CoreIntegration(), "KMPROCESS:ProcessGroup: Error called in core integration mode")) {
return FALSE;
}
DWORD i;
LPKEY kkp = NULL;
PWSTR p;
int sdmfI;
/*
If the number of nested groups goes higher than 50, then break out - this is
a limitation of stack size. This is basically a catch-all for freaky apps that
cause message loopbacks and nasty things like that. Okay, it's really a catch all
for bugs! This means the user's system shouldn't hang.
*/
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
_td->app->QueueDebugInformation(QID_GROUP_ENTER, gp, NULL, NULL, NULL, 0);
sdmfI = -1;
for(i = 0; i < _td->state.lpkb->cxGroupArray; i++)
if(gp == &_td->state.lpkb->dpGroupArray[i])
{
if(_td->state.msg.message == wm_keymankeydown && ShouldDebug(sdmKeyboard))
SendDebugMessageFormat(_td->state.msg.hwnd, sdmKeyboard, 0, "Entering group %d of %d, context '%s'", i+1, _td->state.lpkb->cxGroupArray, getcontext_debug());
sdmfI = i;
break;
}
if(++_td->state.LoopTimes > 50)
{
if(_td->state.msg.message == wm_keymankeydown) SendDebugMessage(_td->state.msg.hwnd, sdmKeyboard, 0, "Aborting output: state.LoopTimes exceeded.");
_td->state.StopOutput = TRUE;
_td->app->QueueDebugInformation(QID_GROUP_EXIT, gp, NULL, NULL, NULL, QID_FLAG_RECURSIVE_OVERFLOW);
return FALSE;
}
_td->state.NoMatches = TRUE;
/*
The rule matching loop.
This loop iterates through all the rules in the group that is currently being
processed. Each rule in a group can be of three different types:
1. A virtual key rule, where the key to be matched is a virtual key
2. A normal key rule (WM_CHAR), where the key to be matched is an Ascii char.
3. A rule in a keyless group, where only the context is matched.
The loop goes through and checks the rules like that. This loop could be optimized
with standard searching techniques - the ContextMatch may be difficult.
*/
if(ShouldDebug(sdmKeyboard))
SendDebugMessageFormat(_td->state.msg.hwnd, sdmKeyboard, 0, "state.vkey: %s shiftFlags: %x; charCode: %X",
Debug_VirtualKey(_td->state.vkey), Globals::get_ShiftState(), _td->state.charCode); // I4582
if(gp)
{
for(kkp = gp->dpKeyArray, i=0; i < gp->cxKeyArray; i++, kkp++)
{
if(!ContextMatch(kkp)) continue;
if(!gp->fUsingKeys)
{
if(kkp->dpContext[0] != 0) break; else continue;
}
//if(kkp->Key == state.vkey)
//SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, 0, "kkp->Key: %d kkp->ShiftFlags: %x",
// kkp->Key, kkp->ShiftFlags);
/* Keyman 6.0: support Virtual Characters */
if(IsEquivalentShift(kkp->ShiftFlags, Globals::get_ShiftState()))
{
if(kkp->Key > VK__MAX && kkp->Key == _td->state.vkey) break; // I3438 // I4582
else if(kkp->Key == _td->state.vkey) break; // I4169
}
else if(kkp->ShiftFlags == 0 && kkp->Key == _td->state.charCode && _td->state.charCode != 0) break;
}
}
if(!gp || i == gp->cxKeyArray)
{
/*
No rule was found that corresponded to the current state of the context and
keyboard. NoMatch should be checked for everything except virtual keys; and
context should also be kept.
If the message was a virtual key, then just return without checking NoMatch.
NoMatch shouldn't be used for virtual keys because it will mean that no key
can ever get through that isn't matched - including arrows, func. keys, etc !!
Context is not kept for virtual keys being output.
*/
if(_td->state.msg.message == wm_keymankeydown && ShouldDebug(sdmKeyboard)) SendDebugMessageFormat(_td->state.msg.hwnd, sdmKeyboard, 0,
"No match was found in group %d of %d", sdmfI, _td->state.lpkb->cxGroupArray);
if(!gp || (_td->state.charCode == 0 && gp->fUsingKeys)) // I4585
// 7.0.241.0: I1133 - Fix mismatched parentheses on state.charCode - ie. we don't want to output this letter if !gp->fUsingKeys
{
BOOL fIsBackspace = _td->state.vkey == VK_BACK && (Globals::get_ShiftState() & (LCTRLFLAG|RCTRLFLAG|LALTFLAG|RALTFLAG)) == 0; // I4128
if(/*_td->app->DebugControlled() &&*/ fIsBackspace) { // I4838 // I4933
if(_td->state.msg.message == wm_keymankeydown) { // I4933
if(!_td->app->IsLegacy()) { // I4933
PWCHAR pdeletecontext = _td->app->ContextBuf(1); // I4933
if(!pdeletecontext || *pdeletecontext == 0) { // I4933
_td->app->ResetContext(); // I4933
fOutputKeystroke = TRUE; // I4933
return FALSE; // I4933
}
if (Uni_IsSurrogate1(*pdeletecontext) && Uni_IsSurrogate2(*(pdeletecontext+1))) {
// 2 backspaces to delete both parts of surrogate pair
// This only needs to be done for TSF-aware apps as legacy apps
// will receive a BKSP WM_KEYDOWN event which results in deleting
// both parts in one action
_td->app->QueueAction(QIT_BACK, BK_BACKSPACE | BK_SURROGATE);
}
else {
_td->app->QueueAction(QIT_BACK, BK_BACKSPACE);
}
}
else {
_td->app->QueueAction(QIT_BACK, BK_BACKSPACE); // I4933
}
}
} else if( (!_td->app->IsLegacy() || !fIsBackspace) && !_td->TIPFPreserved) { // I4024 // I4128 // I4287 // I4290
SendDebugMessageFormat(_td->state.msg.hwnd, sdmKeyboard, 0, " ... IsLegacy = FALSE; IsTIP = TRUE"); // I4128
if(_td->state.charCode == 0) _td->app->ResetContext(); // I3573 // I3577 // I4585
fOutputKeystroke = TRUE;
return FALSE;
}
//fOutputKeystroke = TRUE; return FALSE; // Don't swallow keystroke // I3577
///SendDebugMessageFormat(_td->state.msg.hwnd, sdmKeyboard, 0, " ... IsLegacy = TRUE; IsTIP = TRUE");
/*
If the key is not a character key (white keys), or not processing, then we must init the stack -
unknown keys do things like moving position in the context, so must clear.
*/
if(fIsBackspace) // I4128
{
/*
Must have special handling for VK_BACK: delete a character from the context stack
This only fires if the keyboard has no rule for backspace.
*/
}
else
{
//app->NoSetShift = FALSE;
DWORD dw = _td->state.vkey;
if(dw == 0x05) dw = VK_RETURN; // I649 - VK_ENTER and K_NPENTER
if(_td->state.msg.lParam & (1<<24)) dw |= QVK_EXTENDED; // Extended key flag // I3438
if(_td->state.charCode == 0) {
_td->app->ResetContext();
}
if(_td->TIPFPreserved) { // I4290
if(_td->state.charCode != 0) {
_td->app->QueueAction(QIT_CHAR, _td->state.charCode);
}
} else {
if(_td->state.msg.message == wm_keymankeydown)
{
_td->app->QueueAction(QIT_VSHIFTDOWN, Globals::get_ShiftState()); // 15/05/2001 - fixing I201 -- enabled line
_td->app->QueueAction(QIT_VKEYDOWN, dw);
}
if(_td->state.msg.message == wm_keymankeyup) {
_td->app->QueueAction(QIT_VKEYUP, dw);
_td->app->QueueAction(QIT_VSHIFTUP, Globals::get_ShiftState());
}
}
}
}
else if (gp->dpNoMatch != NULL && *gp->dpNoMatch != 0 && _td->state.msg.message != wm_keymankeyup)
{
/* NoMatch rule found, and is a character key */
_td->app->QueueDebugInformation(QID_NOMATCH_ENTER, gp, NULL, NULL, gp->dpNoMatch, 0);
PostString(gp->dpNoMatch, &_td->state.msg, _td->state.lpkb, NULL);
_td->app->QueueDebugInformation(QID_NOMATCH_EXIT, gp, NULL, NULL, gp->dpNoMatch, 0);
}
else if (_td->state.charCode != 0 && _td->state.charCode != 0xFFFF && _td->state.msg.message != wm_keymankeyup && gp->fUsingKeys)
{
/* No rule found, is a character key */
// 7.0.239.0: I994 - Workaround output order issues - we will use the TSF to output all characters...
// if(app->Type1() == AIType_TIP) { fOutputKeystroke = TRUE; return FALSE; } // Don't swallow keystroke
_td->app->QueueAction(QIT_CHAR, _td->state.charCode);
}
_td->app->QueueDebugInformation(QID_GROUP_EXIT, gp, NULL, NULL, NULL, QID_FLAG_NOMATCH);
return TRUE;
}
if(_td->state.msg.message == wm_keymankeyup)
return TRUE;
SendDebugMessageFormat(_td->state.msg.hwnd, sdmKeyboard, 0, "match found in rule %d", i);
_td->state.NoMatches = FALSE;
/*
Save the context that will be used for output when the 'context' keyword is used.
For each deadkey, we need to add 2 characters; look in related stores as well...
*/
assert(kkp != NULL);
_td->miniContextIfLen = xstrlen(kkp->dpContext) - xstrlen_ignoreifopt(kkp->dpContext);
// 11 Aug 2003 - I25(v6) - mcdurdin - CODE_NUL context support
if(*kkp->dpContext == UC_SENTINEL && *(kkp->dpContext+1) == CODE_NUL)
wcsncpy_s(_td->miniContext, GLOBAL_ContextStackSize, _td->app->ContextBuf(xstrlen_ignoreifopt(kkp->dpContext)-1), GLOBAL_ContextStackSize); // I3162 // I3536
else
wcsncpy_s(_td->miniContext, GLOBAL_ContextStackSize, _td->app->ContextBuf(xstrlen_ignoreifopt(kkp->dpContext)), GLOBAL_ContextStackSize); // I3162 // I3536
_td->miniContext[GLOBAL_ContextStackSize-1] = 0;
_td->app->QueueDebugInformation(QID_RULE_ENTER, gp, kkp, _td->miniContext, NULL, 0);
/*
The next section includes several optimizations that make the code a little harder
to read, but are probably worth it in the time that they save.
If the output string doesn't have a "context" byte at the start, post backspaces
to erase the appropriate number of characters in the application. If it does have
a "context" byte at the start, then the string won't change, and no backspaces are
necessary. You could go one step further with this optimization, in PostAllKeys,
by comparing the starts of the strings to see what is same, and not backspacing
that, but it is probably not necessary.
*/
p = kkp->dpOutput;
if(*p != UC_SENTINEL || *(p+1) != CODE_CONTEXT) {
for(PWSTR mcp = decxstr(wcschr(_td->miniContext, 0), _td->miniContext); mcp != NULL; mcp = decxstr(mcp, _td->miniContext)) {
if (*mcp == UC_SENTINEL) {
switch (*(mcp + 1)) {
case CODE_DEADKEY: _td->app->QueueAction(QIT_BACK, BK_DEADKEY); break;
case CODE_NUL: break; // 11 Aug 2003 - I25(v6) - mcdurdin - CODE_NUL context support
}
}
else if (Uni_IsSurrogate1(*mcp) && Uni_IsSurrogate2(*(mcp + 1))) {
// 2 backspaces to delete both parts of surrogate pair
// This only needs to be done for TSF-aware apps as legacy apps
// will receive a BKSP WM_KEYDOWN event which results in deleting
// both parts in one action
_td->app->QueueAction(QIT_BACK, BK_SURROGATE);
}
else {
_td->app->QueueAction(QIT_BACK, 0);
}
}
}
else {
// otherwise, the "context" entry has to be jumped over
p += 2;
}
/* Use PostString to post the rest of the output string. */
if(PostString(p, &_td->state.msg, _td->state.lpkb, NULL) == psrCheckMatches)
{
_td->app->QueueDebugInformation(QID_RULE_EXIT, gp, kkp, _td->miniContext, NULL, 0);
if(gp->dpMatch && *gp->dpMatch)
{
_td->app->QueueDebugInformation(QID_MATCH_ENTER, gp, NULL, NULL, gp->dpMatch, 0);
PostString(gp->dpMatch, &_td->state.msg, _td->state.lpkb, NULL);
_td->app->QueueDebugInformation(QID_MATCH_EXIT, gp, NULL, NULL, gp->dpMatch, 0);
}
}
else
_td->app->QueueDebugInformation(QID_RULE_EXIT, gp, kkp, _td->miniContext, NULL, 0);
_td->app->QueueDebugInformation(QID_GROUP_EXIT, gp, NULL, NULL, NULL, 0);
return TRUE;
}
/*
* int PostString( LPSTR str, BOOL *useMode, LPMSG mp,
* LPKEYBOARD lpkb );
*
* Parameters: str Pointer to string to send
* useMode Pointer to BOOL about whether a "use" command was found
* mp Pointer to MSG structure to copy in outputting messages
* lpkb Pointer to global keyboard structure
*
* Returns: 0 to continue, 1 and 2 to return.
*
* Called by: ProcessKey
*
* PostString posts a string of "context", "index", "beep", characters and virtual keys
* to the active application, via the Keyman PostKey buffer.
*/
int PostString(PWSTR str, LPMSG mp, LPKEYBOARD lpkb, PWSTR endstr)
{
if (!DebugAssert(!Globals::get_CoreIntegration(), "KKMPROCESS:PostString: Error called in core integration mode")) {
return FALSE;
}
PWSTR p, q, temp;
LPSTORE s;
int n1, n2;
int i, n, shift;
BOOL FoundUse = FALSE;
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
// TODO: Refactor to use incxstr
for(p = str; *p && (p < endstr || !endstr); p++)
{
if(*p == UC_SENTINEL)
switch(*(++p))
{
case CODE_EXTENDED: // Start of a virtual key section w/shift codes
p++;
shift = *p; //(*p<<8) | *(p+1);
_td->app->QueueAction(QIT_VSHIFTDOWN, shift);
p++;
_td->app->QueueAction(QIT_VKEYDOWN, *p);
_td->app->QueueAction(QIT_VKEYUP, *p);
_td->app->QueueAction(QIT_VSHIFTUP, shift);
p++; // CODE_EXTENDEDEND
////// CODE_EXTENDEDEND will be incremented by loop
//app->QueueAction(QIT_VSHIFTUP, shift);
break;
case CODE_DEADKEY: // A deadkey to be output
p++;
_td->app->QueueAction(QIT_DEADKEY, *p);
break;
case CODE_BEEP: // Sound an 'iconasterisk' beep
_td->app->QueueAction(QIT_BELL, 0);
break;
case CODE_CONTEXT: // copy the context to the output
PostString(_td->miniContext, mp, lpkb, wcschr(_td->miniContext, 0));
break;
case CODE_CONTEXTEX:
p++;
for(q = _td->miniContext, i = _td->miniContextIfLen; *q && i < *p-1; i++, q=incxstr(q));
if(*q) {
temp = incxstr(q);
PostString(q, mp, lpkb, temp);
}
break;
case CODE_RETURN: // stop processing and start PostAllKeys
_td->state.StopOutput = TRUE;
return psrPostMessages;
case CODE_CALL:
p++;
CallDLL(_td->lpActiveKeyboard, *p-1);
if(_td->state.StopOutput) return psrPostMessages;
FoundUse = TRUE;
break;
case CODE_USE: // use another group
p++;
ProcessGroup(&lpkb->dpGroupArray[*p-1]);
if(_td->state.StopOutput) return psrPostMessages;
FoundUse = TRUE;
break;
case CODE_CLEARCONTEXT:
_td->app->ResetContext();
_td->app->ReadContext();
break;
case CODE_INDEX:
p++;
s = &_td->lpActiveKeyboard->Keyboard->dpStoreArray[*p - 1];
p++;
n = _td->IndexStack[*p - 1];
for(temp = s->dpString; *temp && n > 0; temp = incxstr(temp), n--);
PostString(temp, mp, lpkb, incxstr(temp));
break;
case CODE_SETOPT:
p++;
n1 = *p - 1;
p++;
n2 = *p - 1;
SetKeyboardOption(_td->lpActiveKeyboard, n1, n2);
break;
case CODE_RESETOPT:
p++;
n1 = *p - 1;
ResetKeyboardOption(_td->lpActiveKeyboard, n1);
break;
case CODE_SAVEOPT:
p++;
n1 = *p - 1;
SaveKeyboardOption(_td->lpActiveKeyboard, n1);
break;
case CODE_IFSYSTEMSTORE:
p+=3;
break;
case CODE_SETSYSTEMSTORE:
p+=2;
break;
}
else
_td->app->QueueAction(QIT_CHAR, *p);
}
return FoundUse ? psrPostMessages : psrCheckMatches;
}
BOOL IsMatchingBaseLayout(PWCHAR layoutName) // I3432
{
BOOL bEqual = _wcsicmp(layoutName, Globals::get_BaseKeyboardName()) == 0 || // I4583
_wcsicmp(layoutName, Globals::get_BaseKeyboardNameAlt()) == 0; // I4583
return bEqual;
}
BOOL IsMatchingPlatformString(PWCHAR platform) // I3432
{
return
_wcsicmp(platform, L"windows") == 0 ||
_wcsicmp(platform, L"desktop") == 0 ||
_wcsicmp(platform, L"hardware") == 0 ||
_wcsicmp(platform, L"native") == 0;
}
BOOL IsMatchingPlatform(LPSTORE s) // I3432
{
PWCHAR t = new WCHAR[wcslen(s->dpString)+1];
wcscpy_s(t, wcslen(s->dpString)+1, s->dpString);
PWCHAR context = NULL;
PWCHAR platform = wcstok_s(t, L" ", &context);
while(platform != NULL)
{
if(!IsMatchingPlatformString(platform))
{
s->dwSystemID = TSS_PLATFORM_NOMATCH;
delete[] t;
return FALSE;
}
platform = wcstok_s(NULL, L" ", &context);
}
s->dwSystemID = TSS_PLATFORM_MATCH;
delete[] t;
return TRUE;
}
/*
* BOOL ContextMatch( LPKEY kkp );
*
* Parameters: kkp Rule to compare
*
* Returns: 0 on OK, 1 on not equal
*
* Called by: ProcessKey
*
* ContextMatch compares the context of a rule with the current context.
*/
BOOL ContextMatch(LPKEY kkp)
{
if (!DebugAssert(!Globals::get_CoreIntegration(), "KMPROCESS:ContextMatch: Error called in core integration mode")) {
return FALSE;
}
WORD /*i,*/ n;
PWSTR p, q, qbuf, temp;
LPWORD indexp;
LPSTORE s, t;
BOOL bEqual;
//SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: ENTER [%d]", kkp->Line);
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
memset(_td->IndexStack, 0, GLOBAL_ContextStackSize*sizeof(WORD)); // I3158 // I3524
p = kkp->dpContext;
if(*p == 0)
{
//SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: EXIT TRUE -> no rule context");
return TRUE;
}
/* 11 Aug 2003 - I25(v6) - mcdurdin - test for CODE_NUL */
if(*p == UC_SENTINEL && *(p+1) == CODE_NUL)
{
// If context buf is longer than the context, then obviously not start of doc.
if(_td->app->ContextBuf(xstrlen_ignoreifopt(p))) return FALSE; // I2484 - Fix bug with if() following nul in same statement
p = incxstr(p);
if(*p == 0) return TRUE;
}
for(PWCHAR pp = p; pp && *pp; pp = incxstr(pp))
{
if(*pp == UC_SENTINEL && *(pp+1) == CODE_IFOPT)
{
s = &_td->lpActiveKeyboard->Keyboard->dpStoreArray[(*(pp+2))-1]; // I2590
t = &_td->lpActiveKeyboard->Keyboard->dpStoreArray[(*(pp+4))-1]; // I2590
bEqual = wcscmp(s->dpString, t->dpString) == 0;
if(*(pp+3) == 1 && bEqual) return FALSE; // I2590
if(*(pp+3) == 2 && !bEqual) return FALSE; // I2590
}
else if(*pp == UC_SENTINEL && *(pp+1) == CODE_IFSYSTEMSTORE) // I3432
{
DWORD dwSystemID = *(pp+2)-1;
t = &_td->lpActiveKeyboard->Keyboard->dpStoreArray[(*(pp+4))-1]; // I2590
switch(dwSystemID)
{
case TSS_PLATFORM_MATCH: // Cached platform result - a matching platform
bEqual = TRUE;
break;
case TSS_PLATFORM_NOMATCH: // Cached platform result - not a matching platform
bEqual = FALSE;
break;
case TSS_PLATFORM:
bEqual = IsMatchingPlatform(t);
break;
case TSS_BASELAYOUT:
bEqual = IsMatchingBaseLayout(t->dpString);
break;
default:
{
PWCHAR ss = GetSystemStore(_td->lpActiveKeyboard->Keyboard, dwSystemID);
if(ss == NULL) return FALSE;
bEqual = wcscmp(ss, t->dpString) == 0;
}
}
if(*(pp+3) == 1 && bEqual) return FALSE; // I2590
if(*(pp+3) == 2 && !bEqual) return FALSE; // I2590
}
}
q = qbuf = _td->app->ContextBuf(xstrlen_ignoreifopt(p));
if(!q)
{
//SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: EXIT FALSE -> context too short");
return FALSE; // context buf is too short!
}
indexp = _td->IndexStack;
//SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: [%d] Rule: %s", kkp->Line, format_unicode_debug(kkp->dpContext));
//SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: [%d] Test: %s", kkp->Line, format_unicode_debug(q));
for(; *p && *q; p = incxstr(p))
{
//SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: p:%x q:%x", *p, *q);
*indexp = 0;
if(*p == UC_SENTINEL)
{
switch(*(p+1))
{
case CODE_DEADKEY:
if(*q != UC_SENTINEL || *(q+1) != CODE_DEADKEY || *(q+2) != *(p+2))
{
// SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: EXIT FALSE -> deadkeys don't match %x %x %x != %x %x %x",
// *p, *(p+1), *(p+2), *q, *(q+1), *(q+2));
return FALSE;
}
break;
case CODE_ANY:
s = &_td->lpActiveKeyboard->Keyboard->dpStoreArray[(*(p+2))-1];
temp = xstrchr(s->dpString, q);
/*SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard ,kkp->Line, "ContextMatch: CODE_ANY [%x %x %x %x %x %x %x %x %x %x] [%x %x %x] %d",
s->dpString[0], s->dpString[1], s->dpString[2],
s->dpString[3], s->dpString[4], s->dpString[5],
s->dpString[6], s->dpString[7], s->dpString[8],
s->dpString[9],
q[0], q[1], q[2],
(temp ? (INT_PTR)(temp-s->dpString) : 0));*/
if(temp != NULL) // I1622
*indexp = (WORD) xstrpos(temp, s->dpString);
//if((temp = xstrchr(s->dpString, GetSuppChar(q))) != NULL)
// *indexp = xstrpos(temp, s->dpString);
else
return FALSE;
break;
case CODE_NOTANY:
s = &_td->lpActiveKeyboard->Keyboard->dpStoreArray[(*(p+2))-1];
if((temp = xstrchr(s->dpString, q)) != NULL) // I1622
return FALSE;
//if((temp = xstrchr(s->dpString, GetSuppChar(q))) != NULL)
// return FALSE;
break;
case CODE_INDEX:
s = &_td->lpActiveKeyboard->Keyboard->dpStoreArray[(*(p+2))-1];
*indexp = n = _td->IndexStack[(*(p+3))-1];
for(temp = s->dpString; *temp && n > 0; temp = incxstr(temp), n--);
if(n != 0) return FALSE;
if(xchrcmp(temp, q) != 0) return FALSE;
////if(GetSuppChar(temp) != GetSuppChar(q)) return FALSE; // I1622
break;
case CODE_CONTEXTEX:
// only the nth character
for(n = *(p+2) - 1, temp = qbuf; temp < q && n > 0; n--, temp = incxstr(temp));
if(n == 0)
if(xchrcmp(temp, q) != 0) return FALSE;
//if(GetSuppChar(temp) != GetSuppChar(q)) return FALSE;
break;
case CODE_IFOPT:
case CODE_IFSYSTEMSTORE: // I3432
indexp++;
continue; // don't increment q
default:
return FALSE;
}
}
else if(xchrcmp(p, q) != 0) //GetSuppChar(p) != GetSuppChar(q))
{
//SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: [%d] FAIL: %s", kkp->Line, format_unicode_debug(p));
//SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: [%d] FAIL: %s", kkp->Line, format_unicode_debug(q));
//SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: EXIT FALSE -> chrs don't match");
return FALSE;
}
indexp++;
q = incxstr(q);
}
while(*p == UC_SENTINEL && (*(p+1) == CODE_IFOPT || *(p+1) == CODE_IFSYSTEMSTORE)) p = incxstr(p); // already tested // I3432
//SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: EXIT %s -> END OF FUNCTION",
// *p == *q ? "TRUE" : "FALSE");
return *p == *q; /*at least one must ==0 at this point*/
}
PWSTR strtowstr(PSTR in)
{
PWSTR result;
@ -933,7 +251,6 @@ PWSTR strtowstr(PSTR in)
return result;
}
PSTR wstrtostr(PCWSTR in)
{
PSTR result;

View file

@ -71,7 +71,7 @@ static BOOL processPersistOpt(
SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessHook: Saving option to registry for keyboard [%s].", activeKeyboard->Name);
LPWSTR value = new WCHAR[sizeof(actionItem->option->value) + 1];
wcscpy_s(value, sizeof(actionItem->option->value) + 1, reinterpret_cast<LPCWSTR>(actionItem->option->value));
SaveKeyboardOptionREGCore(activeKeyboard, reinterpret_cast<LPCWSTR>(actionItem->option->key), value);
SaveKeyboardOptionCoretoRegistry(activeKeyboard, reinterpret_cast<LPCWSTR>(actionItem->option->key), value);
}
}
return TRUE;

View file

@ -37,12 +37,11 @@ struct PreservedKey
class PreservedKeyMap
{
public:
BOOL MapKeyboard(KEYBOARD *pKeyboard, PreservedKey **pPreservedKeys, size_t *cPreservedKeys);
/**
* Updates a map of preserved keys (with GUID) that are used in keyboard rules.
* Passing in NULL for pPreservedKeys will cause cPreservedKeys to be set to the number
* of preserved keys needed for the supplied pKeyboard; this should be used in creating
* pPreservedKeys list to sufficient size. When pPreservedKeys list is passed the
* of preserved keys needed for the supplied pKeyboard; this should be used in creating
* pPreservedKeys list to sufficient size. When pPreservedKeys list is passed the
* cPreservedKeys will be the actual count of the number of unique pPreservedKeys
*
* @param pKeyboard the keyboard for which the rules will be extracted from
@ -50,14 +49,13 @@ public:
* @param cPreservedKeys number of preserved keys in pPreservedKeys - or the size pPreservedKeys needs to be
* @return BOOL return TRUE on success
*/
BOOL MapKeyboardCore(km_kbp_keyboard *pKeyboard, PreservedKey **pPreservedKeys, size_t *cPreservedKeys);
BOOL MapKeyboard(km_kbp_keyboard *pKeyboard, PreservedKey **pPreservedKeys, size_t *cPreservedKeys);
private:
BOOL m_BaseKeyboardUsesAltGr; // I4592
UINT ShiftToTSFShift(UINT ShiftFlags);
BOOL MapUSCharToVK(UINT *puKey, UINT *puShiftFlags);
BOOL MapKeyRule(KEY *pKey, TF_PRESERVEDKEY *pPreservedKey);
BOOL MapKeyRuleCore(km_kbp_keyboard_key *pKeyRule, TF_PRESERVEDKEY *pPreservedKey);
BOOL MapKeyRule(km_kbp_keyboard_key *pKeyRule, TF_PRESERVEDKEY *pPreservedKey);
BOOL IsMatchingKey(PreservedKey *pKey, PreservedKey *pKeys, size_t cKeys);
};
@ -199,45 +197,8 @@ UINT PreservedKeyMap::ShiftToTSFShift(UINT ShiftFlags)
return res;
}
BOOL PreservedKeyMap::MapKeyRule(KEY *pKey, TF_PRESERVEDKEY *pPreservedKey)
{
UINT ShiftFlags;
UINT Key;
Key = pKey->Key;
ShiftFlags = pKey->ShiftFlags;
if(Key == VK_BACK || Key == VK_RETURN || Key == VK_TAB) // I4575
{
//
// We never map backspace, return or tab because these are the only supported virtual key outputs,
// and result in recursion. Sadly, this is an imperfect solution forced upon us by preserved key
// limitations.
//
// Other virtual key output will be blocked with this version.
return FALSE;
}
if (Key > 255) {
//
// Touch-defined keys have a value > 255, but these should never be preserved
//
return FALSE;
}
if(ShiftFlags == 0)
{
if(!MapUSCharToVK(&Key, &ShiftFlags)) return FALSE;
}
pPreservedKey->uVKey = (UINT) USVKToScanCodeToLayoutVK( (WORD) Key); // I3762
pPreservedKey->uModifiers = ShiftToTSFShift(ShiftFlags);
return TRUE;
}
BOOL
PreservedKeyMap::MapKeyRuleCore(km_kbp_keyboard_key *pKeyRule, TF_PRESERVEDKEY *pPreservedKey) {
PreservedKeyMap::MapKeyRule(km_kbp_keyboard_key *pKeyRule, TF_PRESERVEDKEY *pPreservedKey) {
UINT ShiftFlags;
UINT Key;
@ -280,94 +241,8 @@ BOOL PreservedKeyMap::IsMatchingKey(PreservedKey *pKey, PreservedKey *pKeys, siz
return FALSE;
}
// TODO: 5442 - Remove once core processor verfied
BOOL PreservedKeyMap::MapKeyboard(KEYBOARD *pKeyboard, PreservedKey **pPreservedKeys, size_t *cPreservedKeys)
{
size_t cKeys = 0, n;
DWORD i, j;
GROUP *pGroup;
m_BaseKeyboardUsesAltGr = KeyboardGivesCtrlRAltForRAlt(); // I4592
// This is not the same as m_BaseKeyboardUsesAltGr -- we are turning
// the simulation back on after (possibly) turning it off, for a
// consistent experience. TODO: determine if m_BaseKeyboardUseAltGr
// is still needed given we always use kbdus as base for Keyman 10+
BOOL bSimulateAltGr = Globals::get_SimulateAltGr();
// We only want to translate RALT and RALT+SHIFT for Ctrl+Alt rules.
// So we exclude all our other favourite modifier keys.
const UINT RALT_MATCHING_MASK = TF_MOD_CONTROL | TF_MOD_ALT | TF_MOD_LCONTROL | TF_MOD_RCONTROL | TF_MOD_LALT | TF_MOD_RALT;
for(i = 0; i < pKeyboard->cxGroupArray; i++)
{
if(pKeyboard->dpGroupArray[i].fUsingKeys)
{
cKeys += pKeyboard->dpGroupArray[i].cxKeyArray;
}
}
if(cKeys == 0)
{
return FALSE;
}
if (bSimulateAltGr)
{
// We might need twice as many preserved keys to map both LCtrl+LAlt+x and RAlt+x
cKeys *= 2;
}
if(pPreservedKeys == NULL)
{
*cPreservedKeys = cKeys;
return TRUE;
}
if(*cPreservedKeys < cKeys)
{
return FALSE;
}
PreservedKey *pKeys = *pPreservedKeys;
for(n = i = 0; i < pKeyboard->cxGroupArray; i++)
{
pGroup = &pKeyboard->dpGroupArray[i];
if(pGroup->fUsingKeys)
{
for(j = 0; j < pGroup->cxKeyArray; j++)
{
// If we have a key rule for the key, we should preserve it
if(MapKeyRule(&pGroup->dpKeyArray[j], &pKeys[n].key))
{
// Don't attempt to add the same preserved key twice. Bad things happen
if(!IsMatchingKey(&pKeys[n], pKeys, n))
{
CoCreateGuid(&pKeys[n].guid);
n++;
if (bSimulateAltGr && (pKeys[n-1].key.uModifiers & RALT_MATCHING_MASK) == TF_MOD_RALT)
{
// Do this for RALT and RALT+SHIFT only, so we've tested against that mask
// Copy the key and fix modifiers
pKeys[n].key = pKeys[n - 1].key;
pKeys[n].key.uModifiers = (pKeys[n].key.uModifiers & ~TF_MOD_RALT) | TF_MOD_LCONTROL | TF_MOD_LALT;
CoCreateGuid(&pKeys[n].guid);
n++;
}
}
}
}
}
}
*cPreservedKeys = n; // return actual count of allocated keys, usually smaller than allocated count
return TRUE;
}
BOOL
PreservedKeyMap::MapKeyboardCore(km_kbp_keyboard *pKeyboard, PreservedKey **pPreservedKeys, size_t *cPreservedKeys) {
PreservedKeyMap::MapKeyboard(km_kbp_keyboard *pKeyboard, PreservedKey **pPreservedKeys, size_t *cPreservedKeys) {
size_t cKeys = 0, cRules = 0, n = 0;
DWORD i;
@ -422,7 +297,7 @@ PreservedKeyMap::MapKeyboardCore(km_kbp_keyboard *pKeyboard, PreservedKey **pPre
for (i = 0; i < cRules; i++) {
// If we have a key rule for the key, we should preserve it
if (MapKeyRuleCore(&kb_key_list[i], &pKeys[n].key)) {
if (MapKeyRule(&kb_key_list[i], &pKeys[n].key)) {
// Don't attempt to add the same preserved key twice. Bad things happen
if (!IsMatchingKey(&pKeys[n], pKeys, n)) {
CoCreateGuid(&pKeys[n].guid);
@ -457,23 +332,14 @@ extern "C" __declspec(dllexport) BOOL WINAPI GetKeyboardPreservedKeys(PreservedK
if (!_td) {
return FALSE;
}
if (!_td->lpActiveKeyboard) {
return FALSE;
}
// It could be an active core keyboard
if (Globals::get_CoreIntegration()) {
if (!_td->lpActiveKeyboard->lpCoreKeyboard) {
return FALSE;
}
// use api to get key rules
return pkm.MapKeyboardCore(_td->lpActiveKeyboard->lpCoreKeyboard, pPreservedKeys, cPreservedKeys);
} else { // TODO: 5442 Remove else
if (!_td->lpActiveKeyboard->Keyboard) {
return FALSE;
}
return pkm.MapKeyboard(_td->lpActiveKeyboard->Keyboard, pPreservedKeys, cPreservedKeys);
if (!_td->lpActiveKeyboard->lpCoreKeyboard) {
return FALSE;
}
// use api to get key rules
return pkm.MapKeyboard(_td->lpActiveKeyboard->lpCoreKeyboard, pPreservedKeys, cPreservedKeys);
}

View file

@ -53,7 +53,7 @@
// I3594 // I4220
BOOL SelectKeyboardCore(DWORD KeymanID)
BOOL SelectKeyboard(DWORD KeymanID)
{
int i;
HWND hwnd = GetFocus();
@ -71,14 +71,9 @@ BOOL SelectKeyboardCore(DWORD KeymanID)
__try
{
if (_td->ForceFileName[0])
{
SendDebugMessageFormat(hwnd, sdmGlobal, 0, "SelectKeyboard: Ignored due to ForceFile");
return FALSE; // Keyboard file is force-loaded
}
KMHideIM();
if (_td->lpActiveKeyboard) DeactivateDLLs(_td->lpActiveKeyboard);
_td->lpActiveKeyboard = NULL;
_td->ActiveKeymanID = KEYMANID_NONKEYMAN;
@ -101,12 +96,13 @@ BOOL SelectKeyboardCore(DWORD KeymanID)
SendDebugMessageFormat(hwnd, sdmGlobal, 0, "SelectKeyboardCore: NewKeymanID: %x", _td->ActiveKeymanID);
if (_td->app) _td->app->ResetContext();
ResetCapsLock();
// TODO: #5822 tell the core with km_kbp_event so it can reset the capslock state
SelectApplicationIntegration(); // I4287
if (_td->app && !_td->app->IsWindowHandled(hwnd)) _td->app->HandleWindow(hwnd);
_td->state.windowunicode = !_td->app || _td->app->IsUnicode();
ActivateDLLs(_td->lpActiveKeyboard);
return TRUE;
@ -130,88 +126,6 @@ BOOL SelectKeyboardCore(DWORD KeymanID)
return TRUE;
}
BOOL SelectKeyboard(DWORD KeymanID)
{
if (Globals::get_CoreIntegration())
{
return SelectKeyboardCore(KeymanID);
}
int i;
HWND hwnd = GetFocus();
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
SendDebugMessageFormat(hwnd,sdmGlobal,0,"ENTER SelectKeyboard-------------------------------------------");
SendDebugMessageFormat(hwnd,sdmGlobal,0,"ENTER SelectKeyboard: Current:(HKL=%x KeymanID=%x %s) New:(ID=%x)", //lpActiveKeyboard=%s ActiveKeymanID: %x sk: %x KeymanID: %d",
GetKeyboardLayout(0),
_td->ActiveKeymanID,
_td->lpActiveKeyboard == NULL ? "NULL" : _td->lpActiveKeyboard->Name,
//_td->NextKeyboardLayout,
KeymanID);
__try
{
if(_td->ForceFileName[0])
{
SendDebugMessageFormat(hwnd,sdmGlobal,0,"SelectKeyboard: Ignored due to ForceFile");
return FALSE; // Keyboard file is force-loaded
}
KMHideIM();
if(_td->lpActiveKeyboard) DeactivateDLLs(_td->lpActiveKeyboard);
_td->lpActiveKeyboard = NULL;
_td->ActiveKeymanID = KEYMANID_NONKEYMAN;
//SendDebugMessageFormat(hwnd,sdmGlobal,0,"SelectKeyboard: nKeyboards=%d", nKeyboards);
for(i = 0; i < _td->nKeyboards; i++)
{
if(_td->lpKeyboards[i].KeymanID == KeymanID)
{
if(!_td->lpKeyboards[i].Keyboard && !LoadlpKeyboard(i))
{
SendDebugMessageFormat(hwnd,sdmGlobal,0,"SelectKeyboard: Unable to load");
return TRUE;
}
_td->lpActiveKeyboard = &_td->lpKeyboards[i];
_td->ActiveKeymanID = _td->lpActiveKeyboard->KeymanID;
SendDebugMessageFormat(hwnd,sdmGlobal,0,"SelectKeyboard: NewKeymanID: %x", _td->ActiveKeymanID);
if(_td->app) _td->app->ResetContext();
ResetCapsLock();
SelectApplicationIntegration(); // I4287
if(_td->app && !_td->app->IsWindowHandled(hwnd)) _td->app->HandleWindow(hwnd);
_td->state.windowunicode = !_td->app || _td->app->IsUnicode();
ActivateDLLs(_td->lpActiveKeyboard);
return TRUE;
}
}
if(IsFocusedThread())
{
SendDebugMessageFormat(hwnd,sdmGlobal,0,"SelectKeyboard: Keyboard Not Found");
}
}
__finally
{
SendDebugMessageFormat(hwnd,sdmGlobal,0,"EXIT SelectKeyboard: Current:(HKL=%x KeymanID=%x %s) New:(ID=%x)", //lpActiveKeyboard=%s ActiveKeymanID: %x sk: %x KeymanID: %d",
GetKeyboardLayout(0),
_td->ActiveKeymanID,
_td->lpActiveKeyboard == NULL ? "NULL" : _td->lpActiveKeyboard->Name,
KeymanID);
SendDebugMessageFormat(hwnd,sdmGlobal,0,"EXIT SelectKeyboard-------------------------------------------");
}
return TRUE;
}
BOOL SelectKeyboardTSF(DWORD dwIdentity, BOOL foreground) // I3933 // I3949 // I4271
{
if (!foreground && IsFocusedThread()) {

View file

@ -5,77 +5,6 @@
#include <string>
#include <iostream>
// Test UpdateKeyboardOptionsCore, also uses SaveKeyboardOptionsCore
TEST(KEYBOARDOPTIONS, UpdateKeyboardOptionsCore) {
LPINTKEYBOARDINFO kp = new INTKEYBOARDINFO;
memset(kp, 0, sizeof(INTKEYBOARDINFO));
km_kbp_option_item test_env_opts[] = {
{u"__test_point", u"not tiggered", KM_KBP_OPT_KEYBOARD}, {u"hello", u"-", KM_KBP_OPT_ENVIRONMENT}, KM_KBP_OPTIONS_END};
km_kbp_path_name dummyPath = L"dummyActions.mock";
EXPECT_EQ(km_kbp_keyboard_load(dummyPath, &kp->lpCoreKeyboard), KM_KBP_STATUS_OK);
EXPECT_EQ(km_kbp_state_create(kp->lpCoreKeyboard, test_env_opts, &kp->lpCoreKeyboardState), KM_KBP_STATUS_OK);
kp->lpCoreKeyboardOptions = SaveKeyboardOptionsCore(kp);
// No Change
EXPECT_FALSE(UpdateKeyboardOptionsCore(kp->lpCoreKeyboardState, kp->lpCoreKeyboardOptions));
std::u16string value = kp->lpCoreKeyboardOptions[0].value;
std::u16string expectedValue = u"not tiggered";
EXPECT_TRUE(value == expectedValue);
km_kbp_option_item update_key_opts[] = {{u"__test_point", u"triggered", KM_KBP_OPT_KEYBOARD}, KM_KBP_OPTIONS_END};
EXPECT_EQ(km_kbp_state_options_update(kp->lpCoreKeyboardState, update_key_opts), KM_KBP_STATUS_OK);
// Change value to triggered
EXPECT_TRUE(UpdateKeyboardOptionsCore(kp->lpCoreKeyboardState, kp->lpCoreKeyboardOptions));
value = kp->lpCoreKeyboardOptions[0].value;
expectedValue = u"triggered";
EXPECT_TRUE(value == expectedValue);
DisposeKeyboardOptionsCore(&kp->lpCoreKeyboardOptions);
ReleaseStateMemoryCore(&kp->lpCoreKeyboardState);
ReleaseKeyboardMemoryCore(&kp->lpCoreKeyboard);
delete kp;
}
// Test SaveKeyboardOptionsCore and RestoreKeyboardOptionsCORE
TEST(KEYBOARDOPTIONS, SaveRestoreKeyboardOptionsCore) {
LPINTKEYBOARDINFO kp = new INTKEYBOARDINFO;
memset(kp, 0, sizeof(INTKEYBOARDINFO));
km_kbp_option_item test_env_opts[] = {
{u"__test_point", u"not tiggered", KM_KBP_OPT_KEYBOARD}, {u"hello", u"-", KM_KBP_OPT_ENVIRONMENT}, KM_KBP_OPTIONS_END};
km_kbp_path_name dummyPath = L"dummyActions.mock";
EXPECT_EQ(km_kbp_keyboard_load(dummyPath, &kp->lpCoreKeyboard), KM_KBP_STATUS_OK);
EXPECT_EQ(km_kbp_state_create(kp->lpCoreKeyboard, test_env_opts, &kp->lpCoreKeyboardState), KM_KBP_STATUS_OK);
km_kbp_option_item *SavedKBDOptions = SaveKeyboardOptionsCore(kp);
std::u16string value = SavedKBDOptions[0].value;
std::u16string expectedValue = u"not tiggered";
km_kbp_option_item update_key_opts[] = {{u"__test_point", u"triggered", KM_KBP_OPT_KEYBOARD}, KM_KBP_OPTIONS_END};
EXPECT_EQ(km_kbp_state_options_update(kp->lpCoreKeyboardState, update_key_opts), KM_KBP_STATUS_OK);
km_kbp_option_item *NewKBDOptions = SaveKeyboardOptionsCore(kp);
value = NewKBDOptions[0].value;
expectedValue = u"triggered";
EXPECT_TRUE(value == expectedValue);
km_kbp_cp const *retValue = nullptr;
EXPECT_TRUE(RestoreKeyboardOptionsCore(kp->lpCoreKeyboardState, SavedKBDOptions));
EXPECT_EQ(km_kbp_state_option_lookup(kp->lpCoreKeyboardState, KM_KBP_OPT_KEYBOARD, test_env_opts[0].key, &retValue), KM_KBP_STATUS_OK);
value = retValue;
expectedValue = u"not tiggered";
EXPECT_TRUE(value == expectedValue);
DisposeKeyboardOptionsCore(&NewKBDOptions);
DisposeKeyboardOptionsCore(&SavedKBDOptions);
ReleaseStateMemoryCore(&kp->lpCoreKeyboardState);
ReleaseKeyboardMemoryCore(&kp->lpCoreKeyboard);
delete kp;
}
// Test SetupCoreEnvironment and also test km_kbp_state_options_update
TEST(KEYBOARDOPTIONS, SetupCoreEnvironment) {

Some files were not shown because too many files have changed in this diff Show more