Seperate electron and web target building

This commit is contained in:
Farkas József
2017-01-20 02:03:27 +01:00
committed by József Farkas
parent 517aed1b1c
commit 983eb72892
276 changed files with 2154 additions and 95 deletions

View File

@@ -0,0 +1,3 @@
interface Function {
name: string;
}

View File

@@ -0,0 +1,85 @@
# Configuration serializer
This directory contains the configuration serializer of Agent.
The configuration of the UHK is unusually complex for a keyboard, and is composed of a number of items of different types, including keymaps, layers, macros, and the like. This is a supposed to be a short guide for the aspiring hacker. Let's get right into it!
## Setup
Given that the development dependencies are installed on your system you should be able to build the configuration serializer tester by executing `npm run build:test` in this directory, then start the test by running `node test-serializer.js`.
## Configuration representations
There are 3 different representations of the configuration, each filling a specific purpose.
The **JavaScript representation** is optimally suited to be serialized as JSON, and saved to the file system, or transmitted over the network. As a plaintext format, it's human-readable and easily editable. See [uhk-config.json](uhk-config.json) for an example configuration.
The **TypeScript representation** is structurally similar to the JavaScript representation, but it features strongly typed TypeScript objects instead of typeless JavaScript objects. This representation is meant to be used by Agent. Extensive, per-property [assertion](assert.ts) takes place upon initializing the TypeScript objects to ensure the integrity of the configuration.
The **binary representation** is meant to be written to, and read from the EEPROM of the UHK. It's designed to be very compact in order to maximize the use of the 32kbyte EEPROM space.
## Configuration item types
Each configuration item belongs to a specific type. The following types are available:
**Primitive types** are integers of different sizes, and string. See [UhkBuffer](UhkBuffer.ts) which implements all the primitive types.
**Compound types** are composed of primitive types, and/or compound types. All compound types are saved into the [config-items](config-items) directory.
## Dumping serialization
The serialization of configuration items is a complicated business, and many things can go wrong. That's the exact reason why serialization can be dumped to ease debugging. All you have to do is to set `Serializable.enableDump` to `true`, and you'll see something like the following upon serialization actions:
```
KeyActions.fromJsObject: [{"keyActionType":"none"},{"keyActionType":"keystroke","scancode":110},{"keyActionType":"keystrokeModifiers","modifierMask":33},{"keyActionType":"keystrokeWithM...
NoneAction.fromJsObject: {"keyActionType":"none"} => <NoneAction>
KeystrokeAction.fromJsObject: {"keyActionType":"keystroke","scancode":110} => <KeystrokeAction scancode="110">
KeystrokeModifiersAction.fromJsObject: {"keyActionType":"keystrokeModifiers","modifierMask":33} => <KeystrokeModifiersAction modifierMask="33">
KeystrokeWithModifiersAction.fromJsObject: {"keyActionType":"keystrokeWithModifiers","scancode":120,"modifierMask":16} => <KeystrokeWithModifiersAction scancode="120" modifierMask="16">
SwitchLayerAction.fromJsObject: {"keyActionType":"switchLayer","layer":"fn","toggle":false} => <SwitchLayerAction layer="1" toggle="false">
DualRoleKeystrokeAction.fromJsObject: {"keyActionType":"dualRoleKeystroke","scancode":111,"longPressAction":"mod"} => <DualRoleKeystrokeAction scancode="111" longPressAction="8">
MouseAction.fromJsObject: {"keyActionType":"mouse","mouseAction":"scrollDown"} => <MouseAction mouseAction="8">
PlayMacroAction.fromJsObject: {"keyActionType":"playMacro","macroId":0} => <PlayMacroAction macroId="0">
SwitchKeymapAction.fromJsObject: {"keyActionType":"switchKeymap","keymapId":1} => <SwitchKeymapAction keymapId="1">
KeyActions.toBinary: <KeyActions length="9"> => ['u8(9)]
NoneAction.toBinary: <NoneAction> => ['u8(0)]
KeystrokeAction.toBinary: <KeystrokeAction scancode="110"> => ['u8(1), u8(110)]
KeystrokeModifiersAction.toBinary: <KeystrokeModifiersAction modifierMask="33"> => ['u8(2), u8(33)]
KeystrokeWithModifiersAction.toBinary: <KeystrokeWithModifiersAction scancode="120" modifierMask="16"> => ['u8(3), u8(120), u8(16)]
SwitchLayerAction.toBinary: <SwitchLayerAction layer="1" toggle="false"> => ['u8(5), u8(1)]
DualRoleKeystrokeAction.toBinary: <DualRoleKeystrokeAction scancode="111" longPressAction="8"> => ['u8(4), u8(111), u8(8)]
MouseAction.toBinary: <MouseAction mouseAction="8"> => ['u8(7), u8(8)]
PlayMacroAction.toBinary: <PlayMacroAction macroId="0"> => ['u8(8), u8(0)]
SwitchKeymapAction.toBinary: <SwitchKeymapAction keymapId="1"> => ['u8(6), u8(1)]
KeyActions.fromBinary: [u8(9)]
NoneAction.fromBinary: [u8(0)] => <NoneAction>
KeystrokeAction.fromBinary: [u8(1), u8(110)] => <KeystrokeAction scancode="110">
KeystrokeModifiersAction.fromBinary: [u8(2), u8(33)] => <KeystrokeModifiersAction modifierMask="33">
KeystrokeWithModifiersAction.fromBinary: [u8(3), u8(120), u8(16)] => <KeystrokeWithModifiersAction scancode="120" modifierMask="16">
SwitchLayerAction.fromBinary: [u8(5), u8(1)] => <SwitchLayerAction layer="1" toggle="false">
DualRoleKeystrokeAction.fromBinary: [u8(4), u8(111), u8(8)] => <DualRoleKeystrokeAction scancode="111" longPressAction="8">
MouseAction.fromBinary: [u8(7), u8(8)] => <MouseAction mouseAction="8">
PlayMacroAction.fromBinary: [u8(8), u8(0)] => <PlayMacroAction macroId="0">
SwitchKeymapAction.fromBinary: [u8(6), u8(1)] => <SwitchKeymapAction keymapId="1">
KeyActions.toJsObject: <KeyActions length="9">
NoneAction.toJsObject: <NoneAction> => {"keyActionType":"none"}
KeystrokeAction.toJsObject: <KeystrokeAction scancode="110"> => {"keyActionType":"keystroke","scancode":110}
KeystrokeModifiersAction.toJsObject: <KeystrokeModifiersAction modifierMask="33"> => {"keyActionType":"keystrokeModifiers","modifierMask":33}
KeystrokeWithModifiersAction.toJsObject: <KeystrokeWithModifiersAction scancode="120" modifierMask="16"> => {"keyActionType":"keystrokeWithModifiers","scancode":120,"modifierMask":16}
SwitchLayerAction.toJsObject: <SwitchLayerAction layer="1" toggle="false"> => {"keyActionType":"switchLayer","layer":"fn","toggle":false}
DualRoleKeystrokeAction.toJsObject: <DualRoleKeystrokeAction scancode="111" longPressAction="8"> => {"keyActionType":"dualRoleKeystroke","scancode":111,"longPressAction":"mod"}
MouseAction.toJsObject: <MouseAction mouseAction="8"> => {"keyActionType":"mouse","mouseAction":"scrollDown"}
PlayMacroAction.toJsObject: <PlayMacroAction macroId="0"> => {"keyActionType":"playMacro","macroId":0}
SwitchKeymapAction.toJsObject: <SwitchKeymapAction keymapId="1"> => {"keyActionType":"switchKeymap","keymapId":1}
```
## Testing the serializer
[test-serializer.ts](test-serializer.ts) is designed to test the serializer by taking [user-config.json](user-config.json), and transforming it to TypeScript representation, then to binary representation, then finally back to JavaScript representation. This should exercise every major code path.
If the testing is successful the following should be displayed:
```
JSON configurations are identical.
Binary configurations are identical.
```

View File

@@ -0,0 +1,214 @@
export class UhkBuffer {
private static eepromSize = 32 * 1024;
private static maxCompactLength = 0xFFFF;
private static longCompactLengthPrefix = 0xFF;
private static stringEncoding = 'utf8';
private static isFirstElementToDump = false;
offset: number;
private _enableDump = false;
private buffer: Buffer;
private bytesToBacktrack: number;
static simpleElementWriter<T>(buffer: UhkBuffer, element: T): void {
(<any>element).toBinary(buffer); // TODO: Remove any
}
constructor() {
this.offset = 0;
this.bytesToBacktrack = 0;
this.buffer = new Buffer(UhkBuffer.eepromSize);
this.buffer.fill(0);
}
readInt8(): number {
let value = this.buffer.readInt8(this.offset);
this.dump(`i8(${value})`);
this.bytesToBacktrack = 1;
this.offset += this.bytesToBacktrack;
return value;
}
writeInt8(value: number): void {
this.dump(`i8(${value})`);
this.buffer.writeInt8(value, this.offset);
this.offset += 1;
}
readUInt8(): number {
let value = this.buffer.readUInt8(this.offset);
this.dump(`u8(${value})`);
this.bytesToBacktrack = 1;
this.offset += this.bytesToBacktrack;
return value;
}
writeUInt8(value: number): void {
this.dump(`u8(${value})`);
this.buffer.writeUInt8(value, this.offset);
this.offset += 1;
}
readInt16(): number {
let value = this.buffer.readInt16LE(this.offset);
this.dump(`i16(${value})`);
this.bytesToBacktrack = 2;
this.offset += this.bytesToBacktrack;
return value;
}
writeInt16(value: number): void {
this.dump(`i16(${value})`);
this.buffer.writeInt16LE(value, this.offset);
this.offset += 2;
}
readUInt16(): number {
let value = this.buffer.readUInt16LE(this.offset);
this.dump(`u16(${value})`);
this.bytesToBacktrack = 2;
this.offset += this.bytesToBacktrack;
return value;
}
writeUInt16(value: number): void {
this.dump(`u16(${value})`);
this.buffer.writeUInt16LE(value, this.offset);
this.offset += 2;
}
readInt32(): number {
let value = this.buffer.readInt32LE(this.offset);
this.dump(`i32(${value})`);
this.bytesToBacktrack = 4;
this.offset += this.bytesToBacktrack;
return value;
}
writeInt32(value: number): void {
this.dump(`i32(${value})`);
this.buffer.writeInt32LE(value, this.offset);
this.offset += 4;
}
readUInt32(): number {
let value = this.buffer.readUInt32LE(this.offset);
this.dump(`u32(${value})`);
this.bytesToBacktrack = 4;
this.offset += this.bytesToBacktrack;
return value;
}
writeUInt32(value: number): void {
this.dump(`u32(${value})`);
this.buffer.writeUInt32LE(value, this.offset);
this.offset += 4;
}
readCompactLength(): number {
let length = this.readUInt8();
if (length === UhkBuffer.longCompactLengthPrefix) {
length += this.readUInt8() << 8;
}
return length;
}
writeCompactLength(length: number) {
if (length >= UhkBuffer.longCompactLengthPrefix) {
this.writeUInt8(UhkBuffer.longCompactLengthPrefix);
this.writeUInt16(length);
} else {
this.writeUInt8(length);
}
}
readString(): string {
let stringByteLength = this.readCompactLength();
let str = this.buffer.toString(UhkBuffer.stringEncoding, this.offset, this.offset + stringByteLength);
this.dump(`${UhkBuffer.stringEncoding}(${str})`);
this.bytesToBacktrack = stringByteLength;
this.offset += stringByteLength;
return str;
}
writeString(str: string): void {
let stringByteLength = Buffer.byteLength(str, UhkBuffer.stringEncoding);
if (stringByteLength > UhkBuffer.maxCompactLength) {
throw `Cannot serialize string: ${stringByteLength} bytes is larger
than the maximum allowed length of ${UhkBuffer.maxCompactLength} bytes`;
}
this.writeCompactLength(stringByteLength);
this.dump(`${UhkBuffer.stringEncoding}(${str})`);
this.buffer.write(str, this.offset, stringByteLength, UhkBuffer.stringEncoding);
this.offset += stringByteLength;
}
readBoolean(): boolean {
return this.readUInt8() !== 0;
}
writeBoolean(bool: boolean) {
this.writeUInt8(bool ? 1 : 0);
}
readArray<T>(elementReader: (buffer: UhkBuffer, index?: number) => T): T[] {
let array: T[] = [];
let length = this.readCompactLength();
for (let i = 0; i < length; ++i) {
array.push(elementReader(this, i));
}
return array;
}
writeArray<T>(
array: T[],
elementWriter: (buffer: UhkBuffer, element: T, index?: number) => void = UhkBuffer.simpleElementWriter
): void {
const length = array.length;
this.writeCompactLength(length);
for (let i = 0; i < length; ++i) {
elementWriter(this, array[i], i);
}
}
backtrack(): void {
this.offset -= this.bytesToBacktrack;
this.bytesToBacktrack = 0;
}
getBufferContent(): Buffer {
return this.buffer.slice(0, this.offset);
}
get enableDump() {
return this._enableDump;
}
set enableDump(value) {
if (value) {
UhkBuffer.isFirstElementToDump = true;
}
this._enableDump = value;
}
dump(value: any) {
if (!this.enableDump) {
return;
}
if (!UhkBuffer.isFirstElementToDump) {
process.stdout.write(', ');
}
process.stdout.write(value);
if (UhkBuffer.isFirstElementToDump) {
UhkBuffer.isFirstElementToDump = false;
}
}
}

View File

@@ -0,0 +1,78 @@
export function assertUInt8(target: any, key: string) {
return assertInteger(target, key, 0, 0xFF);
}
export function assertInt8(target: any, key: string) {
return assertInteger(target, key, -0x80, 0x7F);
}
export function assertUInt16(target: any, key: string) {
return assertInteger(target, key, 0, 0xFFFF);
}
export function assertInt16(target: any, key: string) {
return assertInteger(target, key, -0x8000, 0x7FFF);
}
export function assertUInt32(target: any, key: string) {
return assertInteger(target, key, 0, 0xFFFFFFFF);
}
export function assertInt32(target: any, key: string) {
return assertInteger(target, key, -0x80000000, 0x7FFFFFFF);
}
export function assertCompactLength(target: any, key: string) {
return assertUInt16(target, key);
}
function assertInteger(target: any, key: string, min: number, max: number) {
const priv = '_' + key;
function getter() {
return this[priv];
}
function setter(newVal: any) {
if (this[priv] !== newVal) {
if (newVal < min || newVal > max) {
throw `${target.constructor.name}.${key}: ` +
`Integer ${newVal} is outside the valid [${min}, ${max}] interval`;
}
this[priv] = newVal;
}
}
Object.defineProperty(target, key, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
export function assertEnum<E>(enumerated: E) {
return function(target: any, key: string) {
const priv = '_' + key;
function getter() {
return this[priv];
}
function setter(newVal: any) {
if (this[priv] !== newVal) {
if (enumerated[newVal] === undefined) {
throw `${target.constructor.name}.${key}: ${newVal} is not enum`;
}
this[priv] = newVal;
}
}
Object.defineProperty(target, key, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
};
}

View File

@@ -0,0 +1,65 @@
import { assertUInt8 } from '../assert';
import { UhkBuffer } from '../UhkBuffer';
export class HardwareConfiguration {
signature: string;
@assertUInt8
dataModelVersion: number;
@assertUInt8
hardwareId: number;
@assertUInt8
brandId: number;
isIso: boolean;
hasBacklighting: boolean;
fromJsonObject(jsonObject: any): HardwareConfiguration {
this.signature = jsonObject.signature;
this.dataModelVersion = jsonObject.dataModelVersion;
this.hardwareId = jsonObject.hardwareId;
this.brandId = jsonObject.brandId;
this.isIso = jsonObject.isIso;
this.hasBacklighting = jsonObject.hasBacklighting;
return this;
}
fromBinary(buffer: UhkBuffer): HardwareConfiguration {
this.signature = buffer.readString();
this.dataModelVersion = buffer.readUInt16();
this.hardwareId = buffer.readUInt8();
this.brandId = buffer.readUInt8();
this.isIso = buffer.readBoolean();
this.hasBacklighting = buffer.readBoolean();
return this;
}
toJsonObject(): any {
return {
signature: this.signature,
dataModelVersion: this.dataModelVersion,
hardwareId: this.hardwareId,
brandId: this.brandId,
isIso: this.isIso,
hasBacklighting: this.hasBacklighting
};
}
toBinary(buffer: UhkBuffer): void {
buffer.writeString(this.signature);
buffer.writeUInt16(this.dataModelVersion);
buffer.writeUInt8(this.hardwareId);
buffer.writeUInt8(this.brandId);
buffer.writeBoolean(this.isIso);
buffer.writeBoolean(this.hasBacklighting);
}
toString(): string {
return `<HardwareConfiguration signature="${this.signature}">`;
}
}

View File

@@ -0,0 +1,10 @@
export enum KeyModifiers {
leftCtrl = 1 << 0,
leftShift = 1 << 1,
leftAlt = 1 << 2,
leftGui = 1 << 3,
rightCtrl = 1 << 4,
rightShift = 1 << 5,
rightAlt = 1 << 6,
rightGui = 1 << 7
}

View File

@@ -0,0 +1,72 @@
import { UhkBuffer } from '../UhkBuffer';
import { Layer } from './Layer';
import { Macro } from './Macro';
export class Keymap {
name: string;
description: string;
abbreviation: string;
isDefault: boolean;
layers: Layer[];
constructor(keymap?: Keymap) {
if (!keymap) {
return;
}
this.name = keymap.name;
this.description = keymap.description;
this.abbreviation = keymap.abbreviation;
this.isDefault = keymap.isDefault;
this.layers = keymap.layers.map(layer => new Layer(layer));
}
fromJsonObject(jsonObject: any, macros?: Macro[]): Keymap {
this.isDefault = jsonObject.isDefault;
this.abbreviation = jsonObject.abbreviation;
this.name = jsonObject.name;
this.description = jsonObject.description;
this.layers = jsonObject.layers.map((layer: any) => new Layer().fromJsonObject(layer, macros));
return this;
}
fromBinary(buffer: UhkBuffer, macros?: Macro[]): Keymap {
this.abbreviation = buffer.readString();
this.isDefault = buffer.readBoolean();
this.name = buffer.readString();
this.description = buffer.readString();
this.layers = buffer.readArray<Layer>(uhkBuffer => {
return new Layer().fromBinary(uhkBuffer, macros);
});
return this;
}
toJsonObject(macros?: Macro[]): any {
return {
isDefault: this.isDefault,
abbreviation: this.abbreviation,
name: this.name,
description: this.description,
layers: this.layers.map(layer => layer.toJsonObject(macros))
};
}
toBinary(buffer: UhkBuffer, macros?: Macro[]): void {
buffer.writeString(this.abbreviation);
buffer.writeBoolean(this.isDefault);
buffer.writeString(this.name);
buffer.writeString(this.description);
buffer.writeArray(this.layers, (uhkBuffer: UhkBuffer, layer: Layer) => {
layer.toBinary(uhkBuffer, macros);
});
}
toString(): string {
return `<Keymap abbreviation="${this.abbreviation}" name="${this.name}">`;
}
}

View File

@@ -0,0 +1,45 @@
import { UhkBuffer } from '../UhkBuffer';
import { Keymap } from './Keymap';
import { Macro } from './Macro';
import { Module } from './Module';
export class Layer {
modules: Module[];
constructor(layers?: Layer) {
if (!layers) {
return;
}
this.modules = layers.modules.map(module => new Module(module));
}
fromJsonObject(jsonObject: any, macros?: Macro[]): Layer {
this.modules = jsonObject.modules.map((module: any) => new Module().fromJsonObject(module, macros));
return this;
}
fromBinary(buffer: UhkBuffer, macros?: Macro[]): Layer {
this.modules = buffer.readArray<Module>(uhkBuffer => {
return new Module().fromBinary(uhkBuffer, macros);
});
return this;
}
toJsonObject(macros?: Macro[]): any {
return {
modules: this.modules.map(module => module.toJsonObject(macros))
};
}
toBinary(buffer: UhkBuffer, macros?: Macro[]): void {
buffer.writeArray(this.modules, (uhkBuffer: UhkBuffer, module: Module) => {
module.toBinary(uhkBuffer, macros);
});
}
toString(): string {
return `<Layer>`;
}
}

View File

@@ -0,0 +1,13 @@
export enum LongPressAction {
leftCtrl,
leftShift,
leftAlt,
leftSuper,
rightCtrl,
rightShift,
rightAlt,
rightSuper,
mod,
fn,
mouse
};

View File

@@ -0,0 +1,68 @@
import { assertUInt8 } from '../assert';
import { UhkBuffer } from '../UhkBuffer';
import { Helper as MacroActionHelper, MacroAction } from './macro-action';
export class Macro {
@assertUInt8
id: number;
isLooped: boolean;
isPrivate: boolean;
name: string;
macroActions: MacroAction[];
constructor(other?: Macro) {
if (!other) {
return;
}
this.id = other.id;
this.isLooped = other.isLooped;
this.isPrivate = other.isPrivate;
this.name = other.name;
this.macroActions = other.macroActions.map(macroAction => MacroActionHelper.createMacroAction(macroAction));
}
fromJsonObject(jsonObject: any): Macro {
this.isLooped = jsonObject.isLooped;
this.isPrivate = jsonObject.isPrivate;
this.name = jsonObject.name;
this.macroActions = jsonObject.macroActions.map((macroAction: any) => MacroActionHelper.createMacroAction(macroAction));
return this;
}
fromBinary(buffer: UhkBuffer): Macro {
this.isLooped = buffer.readBoolean();
this.isPrivate = buffer.readBoolean();
this.name = buffer.readString();
let macroActionsLength: number = buffer.readCompactLength();
this.macroActions = [];
for (let i = 0; i < macroActionsLength; ++i) {
this.macroActions.push(MacroActionHelper.createMacroAction(buffer));
}
return this;
}
toJsonObject(): any {
return {
isLooped: this.isLooped,
isPrivate: this.isPrivate,
name: this.name,
macroActions: this.macroActions.map(macroAction => macroAction.toJsonObject())
};
}
toBinary(buffer: UhkBuffer): void {
buffer.writeBoolean(this.isLooped);
buffer.writeBoolean(this.isPrivate);
buffer.writeString(this.name);
buffer.writeArray(this.macroActions);
}
toString(): string {
return `<Macro id="${this.id}" name="${this.name}">`;
}
}

View File

@@ -0,0 +1,86 @@
import { assertEnum, assertUInt8 } from '../assert';
import { UhkBuffer } from '../UhkBuffer';
import { Helper as KeyActionHelper, KeyAction, NoneAction } from './key-action';
import { Keymap } from './Keymap';
import { Macro } from './Macro';
enum PointerRole {
none,
move,
scroll
}
export class Module {
@assertUInt8
id: number;
keyActions: KeyAction[];
@assertEnum(PointerRole)
pointerRole: PointerRole;
constructor(other?: Module) {
if (!other) {
return;
}
this.id = other.id;
this.keyActions = other.keyActions.map(keyAction => KeyActionHelper.createKeyAction(keyAction));
this.pointerRole = other.pointerRole;
}
fromJsonObject(jsonObject: any, macros?: Macro[]): Module {
this.id = jsonObject.id;
this.pointerRole = PointerRole[<string>jsonObject.pointerRole];
this.keyActions = jsonObject.keyActions.map((keyAction: any) => {
return KeyActionHelper.createKeyAction(keyAction, macros);
});
return this;
}
fromBinary(buffer: UhkBuffer, macros?: Macro[]): Module {
this.id = buffer.readUInt8();
this.pointerRole = buffer.readUInt8();
let keyActionsLength: number = buffer.readCompactLength();
this.keyActions = [];
for (let i = 0; i < keyActionsLength; ++i) {
this.keyActions.push(KeyActionHelper.createKeyAction(buffer, macros));
}
return this;
}
toJsonObject(macros?: Macro[]): any {
return {
id: this.id,
pointerRole: PointerRole[this.pointerRole],
keyActions: this.keyActions.map(keyAction => {
if (keyAction) {
return keyAction.toJsonObject(macros);
}
})
};
}
toBinary(buffer: UhkBuffer, macros?: Macro[]): void {
buffer.writeUInt8(this.id);
buffer.writeUInt8(this.pointerRole);
const noneAction = new NoneAction();
const keyActions: KeyAction[] = this.keyActions.map(keyAction => {
if (keyAction) {
return keyAction;
}
return noneAction;
});
buffer.writeArray(keyActions, (uhkBuffer: UhkBuffer, keyAction: KeyAction) => {
keyAction.toBinary(uhkBuffer, macros);
});
}
toString(): string {
return `<Module id="${this.id}" pointerRole="${this.pointerRole}">`;
}
}

View File

@@ -0,0 +1,58 @@
import { assertUInt8 } from '../assert';
import { UhkBuffer } from '../UhkBuffer';
export class ModuleConfiguration {
/*
* module id enumeration is a separate story
*/
@assertUInt8
id: number;
@assertUInt8
initialPointerSpeed: number;
@assertUInt8
pointerAcceleration: number;
@assertUInt8
maxPointerSpeed: number;
fromJsonObject(jsonObject: any): ModuleConfiguration {
this.id = jsonObject.id;
this.initialPointerSpeed = jsonObject.initialPointerSpeed;
this.pointerAcceleration = jsonObject.pointerAcceleration;
this.maxPointerSpeed = jsonObject.maxPointerSpeed;
return this;
}
fromBinary(buffer: UhkBuffer): ModuleConfiguration {
this.id = buffer.readUInt8();
this.initialPointerSpeed = buffer.readUInt8();
this.pointerAcceleration = buffer.readUInt8();
this.maxPointerSpeed = buffer.readUInt8();
return this;
}
toJsonObject(): any {
return {
id: this.id,
initialPointerSpeed: this.initialPointerSpeed,
pointerAcceleration: this.pointerAcceleration,
maxPointerSpeed: this.maxPointerSpeed
};
}
toBinary(buffer: UhkBuffer): void {
buffer.writeUInt8(this.id);
buffer.writeUInt8(this.initialPointerSpeed);
buffer.writeUInt8(this.pointerAcceleration);
buffer.writeUInt8(this.maxPointerSpeed);
}
toString(): string {
return `<ModuleConfiguration id="${this.id}" >`;
}
}

View File

@@ -0,0 +1,76 @@
import { assertUInt16 } from '../assert';
import { UhkBuffer } from '../UhkBuffer';
import { Keymap } from './Keymap';
import { Macro } from './Macro';
import { ModuleConfiguration } from './ModuleConfiguration';
export class UserConfiguration {
@assertUInt16
dataModelVersion: number;
moduleConfigurations: ModuleConfiguration[];
keymaps: Keymap[];
macros: Macro[];
fromJsonObject(jsonObject: any): UserConfiguration {
this.dataModelVersion = jsonObject.dataModelVersion;
this.moduleConfigurations = jsonObject.moduleConfigurations.map((moduleConfiguration: any) => {
return new ModuleConfiguration().fromJsonObject(moduleConfiguration);
});
this.macros = jsonObject.macros.map((macroJsonObject: any, index: number) => {
const macro = new Macro().fromJsonObject(macroJsonObject);
macro.id = index;
return macro;
});
this.keymaps = jsonObject.keymaps.map((keymap: any) => new Keymap().fromJsonObject(keymap, this.macros));
return this;
}
fromBinary(buffer: UhkBuffer): UserConfiguration {
this.dataModelVersion = buffer.readUInt16();
this.moduleConfigurations = buffer.readArray<ModuleConfiguration>(uhkBuffer => {
return new ModuleConfiguration().fromBinary(uhkBuffer);
});
this.macros = buffer.readArray<Macro>((uhkBuffer, index) => {
const macro = new Macro().fromBinary(uhkBuffer);
macro.id = index;
return macro;
});
this.keymaps = buffer.readArray<Keymap>(uhkBuffer => new Keymap().fromBinary(uhkBuffer, this.macros));
return this;
}
toJsonObject(): any {
return {
dataModelVersion: this.dataModelVersion,
moduleConfigurations: this.moduleConfigurations.map(moduleConfiguration => moduleConfiguration.toJsonObject()),
keymaps: this.keymaps.map(keymap => keymap.toJsonObject(this.macros)),
macros: this.macros.map(macro => macro.toJsonObject())
};
}
toBinary(buffer: UhkBuffer): void {
buffer.writeUInt16(this.dataModelVersion);
buffer.writeArray(this.moduleConfigurations);
buffer.writeArray(this.macros);
buffer.writeArray(this.keymaps, (uhkBuffer: UhkBuffer, keymap: Keymap) => {
keymap.toBinary(uhkBuffer, this.macros);
});
}
toString(): string {
return `<UserConfiguration dataModelVersion="${this.dataModelVersion}">`;
}
getKeymap(keymapAbbreviation: string): Keymap {
return this.keymaps.find(keymap => keymapAbbreviation === keymap.abbreviation);
}
getMacro(macroId: number): Macro {
return this.macros.find(macro => macroId === macro.id);
}
}

View File

@@ -0,0 +1,59 @@
/// <reference path="../../Function.d.ts" />
import { Macro } from '../Macro';
import { UhkBuffer } from '../../UhkBuffer';
export enum KeyActionId {
NoneAction = 0,
KeystrokeAction = 1,
/*
1 - 7 are reserved for KeystrokeAction
3 bits:
1: Do we have scancode?
2: Do we have modifiers?
3: Do we have longpress?
*/
LastKeystrokeAction = 7, // TODO: remove this after refactoring the keyActionId check
SwitchLayerAction = 8,
SwitchKeymapAction = 9,
MouseAction = 10,
PlayMacroAction = 11
}
export let keyActionType = {
NoneAction : 'none',
KeystrokeAction : 'keystroke',
SwitchLayerAction : 'switchLayer',
SwitchKeymapAction : 'switchKeymap',
MouseAction : 'mouse',
PlayMacroAction : 'playMacro'
};
export abstract class KeyAction {
assertKeyActionType(jsObject: any): void {
let keyActionClassname: string = this.constructor.name;
let keyActionTypeString: string = keyActionType[keyActionClassname];
if (jsObject.keyActionType !== keyActionTypeString) {
throw `Invalid ${keyActionClassname}.keyActionType: ${jsObject.keyActionType}`;
}
}
readAndAssertKeyActionId(buffer: UhkBuffer): KeyActionId {
let classname: string = this.constructor.name;
let readKeyActionId: number = buffer.readUInt8();
let keyActionId: number = KeyActionId[classname];
if (keyActionId === KeyActionId.KeystrokeAction) {
if (readKeyActionId < KeyActionId.KeystrokeAction || readKeyActionId > KeyActionId.LastKeystrokeAction) {
throw `Invalid ${classname} first byte: ${readKeyActionId}`;
}
} else if (readKeyActionId !== keyActionId) {
throw `Invalid ${classname} first byte: ${readKeyActionId}`;
}
return readKeyActionId;
}
abstract toJsonObject(macros?: Macro[]): any;
abstract toBinary(buffer: UhkBuffer, macros?: Macro[]): any;
}

View File

@@ -0,0 +1,156 @@
import { assertEnum, assertUInt8 } from '../../assert';
import { UhkBuffer } from '../../UhkBuffer';
import { KeyModifiers } from '../KeyModifiers';
import { LongPressAction } from '../LongPressAction';
import { KeyAction, KeyActionId, keyActionType } from './KeyAction';
export enum KeystrokeActionFlag {
scancode = 1 << 0,
modifierMask = 1 << 1,
longPressAction = 1 << 2
}
interface JsonObjectKeystrokeAction {
keyActionType: string;
scancode?: number;
modifierMask?: number;
longPressAction?: string;
}
const MODIFIERS = ['LCtrl', 'LShift', 'LAlt', 'LSuper', 'RCtrl', 'RShift', 'RAlt', 'RSuper'];
export class KeystrokeAction extends KeyAction {
@assertUInt8
scancode: number;
@assertUInt8
modifierMask: number;
@assertEnum(LongPressAction)
longPressAction: LongPressAction;
constructor(other?: KeystrokeAction) {
super();
if (!other) {
return;
}
this.scancode = other.scancode;
this.modifierMask = other.modifierMask;
this.longPressAction = other.longPressAction;
}
fromJsonObject(jsonObject: JsonObjectKeystrokeAction): KeystrokeAction {
this.assertKeyActionType(jsonObject);
this.scancode = jsonObject.scancode;
this.modifierMask = jsonObject.modifierMask;
this.longPressAction = LongPressAction[jsonObject.longPressAction];
return this;
}
fromBinary(buffer: UhkBuffer): KeystrokeAction {
let keyActionId: KeyActionId = this.readAndAssertKeyActionId(buffer);
let flags: number = keyActionId - KeyActionId.KeystrokeAction;
if (flags & KeystrokeActionFlag.scancode) {
this.scancode = buffer.readUInt8();
}
if (flags & KeystrokeActionFlag.modifierMask) {
this.modifierMask = buffer.readUInt8();
}
if (flags & KeystrokeActionFlag.longPressAction) {
this.longPressAction = buffer.readUInt8();
}
return this;
}
toJsonObject(): JsonObjectKeystrokeAction {
let jsonObject: JsonObjectKeystrokeAction = {
keyActionType: keyActionType.KeystrokeAction
};
if (this.hasScancode()) {
jsonObject.scancode = this.scancode;
}
if (this.hasActiveModifier()) {
jsonObject.modifierMask = this.modifierMask;
}
if (this.hasLongPressAction()) {
jsonObject.longPressAction = LongPressAction[this.longPressAction];
}
return jsonObject;
}
toBinary(buffer: UhkBuffer) {
let flags = 0;
let bufferData: number[] = [];
if (this.hasScancode()) {
flags |= KeystrokeActionFlag.scancode;
bufferData.push(this.scancode);
}
if (this.hasActiveModifier()) {
flags |= KeystrokeActionFlag.modifierMask;
bufferData.push(this.modifierMask);
}
if (this.hasLongPressAction()) {
flags |= KeystrokeActionFlag.longPressAction;
bufferData.push(this.longPressAction);
}
buffer.writeUInt8(KeyActionId.KeystrokeAction + flags);
for (let i = 0; i < bufferData.length; ++i) {
buffer.writeUInt8(bufferData[i]);
}
}
toString(): string {
let properties: string[] = [];
if (this.hasScancode()) {
properties.push(`scancode="${this.scancode}"`);
}
if (this.hasActiveModifier()) {
properties.push(`modifierMask="${this.modifierMask}"`);
}
if (this.hasLongPressAction()) {
properties.push(`longPressAction="${this.longPressAction}"`);
}
return `<KeystrokeAction ${properties.join(' ')}>`;
}
isActive(modifier: KeyModifiers): boolean {
return (this.modifierMask & modifier) > 0;
}
hasActiveModifier(): boolean {
return this.modifierMask > 0;
}
hasLongPressAction(): boolean {
return this.longPressAction !== undefined;
}
hasScancode(): boolean {
return !!this.scancode;
}
hasOnlyOneActiveModifier(): boolean {
return this.modifierMask !== 0 && !(this.modifierMask & this.modifierMask - 1);
}
getModifierList(): string[] {
let modifierList: string[] = [];
let modifierMask = this.modifierMask;
for (let i = 0; modifierMask !== 0; ++i, modifierMask >>= 1) {
if (modifierMask & 1) {
modifierList.push(MODIFIERS[i]);
}
}
return modifierList;
}
}

View File

@@ -0,0 +1,61 @@
import { assertEnum } from '../../assert';
import { UhkBuffer } from '../../UhkBuffer';
import { KeyAction, KeyActionId, keyActionType } from './KeyAction';
export enum MouseActionParam {
leftClick,
middleClick,
rightClick,
moveUp,
moveDown,
moveLeft,
moveRight,
scrollUp,
scrollDown,
scrollLeft,
scrollRight,
accelerate,
decelerate
}
export class MouseAction extends KeyAction {
@assertEnum(MouseActionParam)
mouseAction: MouseActionParam;
constructor(other?: MouseAction) {
super();
if (!other) {
return;
}
this.mouseAction = other.mouseAction;
}
fromJsonObject(jsObject: any): MouseAction {
this.assertKeyActionType(jsObject);
this.mouseAction = MouseActionParam[<string>jsObject.mouseAction];
return this;
}
fromBinary(buffer: UhkBuffer): MouseAction {
this.readAndAssertKeyActionId(buffer);
this.mouseAction = buffer.readUInt8();
return this;
}
toJsonObject(): any {
return {
keyActionType: keyActionType.MouseAction,
mouseAction: MouseActionParam[this.mouseAction]
};
}
toBinary(buffer: UhkBuffer) {
buffer.writeUInt8(KeyActionId.MouseAction);
buffer.writeUInt8(this.mouseAction);
}
toString(): string {
return `<MouseAction mouseAction="${this.mouseAction}">`;
}
}

View File

@@ -0,0 +1,35 @@
import { UhkBuffer } from '../../UhkBuffer';
import { KeyAction, KeyActionId, keyActionType } from './KeyAction';
/**
* NoneAction is only intended for binary serialization of undefined key actions
* DO NOT use it as a real KeyAction
*
*/
export class NoneAction extends KeyAction {
fromJsonObject(jsonObject: any): NoneAction {
this.assertKeyActionType(jsonObject);
return this;
}
fromBinary(buffer: UhkBuffer): NoneAction {
this.readAndAssertKeyActionId(buffer);
return this;
}
toJsonObject(): any {
return {
keyActionType: keyActionType.NoneAction
};
}
toBinary(buffer: UhkBuffer) {
buffer.writeUInt8(KeyActionId.NoneAction);
}
toString(): string {
return '<NoneAction>';
}
}

View File

@@ -0,0 +1,51 @@
import { assertUInt8 } from '../../assert';
import { UhkBuffer } from '../../UhkBuffer';
import { Macro } from '../Macro';
import { KeyAction, KeyActionId, keyActionType } from './KeyAction';
export class PlayMacroAction extends KeyAction {
@assertUInt8
macroId: number;
constructor(parameter?: PlayMacroAction | Macro) {
super();
if (!parameter) {
return;
}
if (parameter instanceof PlayMacroAction) {
this.macroId = parameter.macroId;
} else {
this.macroId = parameter.id;
}
}
fromJsonObject(jsonObject: any, macros: Macro[]): PlayMacroAction {
this.assertKeyActionType(jsonObject);
this.macroId = macros[jsonObject.macroIndex].id;
return this;
}
fromBinary(buffer: UhkBuffer, macros: Macro[]): PlayMacroAction {
this.readAndAssertKeyActionId(buffer);
const macroIndex = buffer.readUInt8();
this.macroId = macros[macroIndex].id;
return this;
}
toJsonObject(macros: Macro[]): any {
return {
keyActionType: keyActionType.PlayMacroAction,
macroIndex: macros.findIndex(macro => macro.id === this.macroId)
};
}
toBinary(buffer: UhkBuffer, macros: Macro[]) {
buffer.writeUInt8(KeyActionId.PlayMacroAction);
buffer.writeUInt8(macros.findIndex(macro => macro.id === this.macroId));
}
toString(): string {
return `<PlayMacroAction macroId="${this.macroId}">`;
}
}

View File

@@ -0,0 +1,48 @@
import { UhkBuffer } from '../../UhkBuffer';
import { Keymap } from '../Keymap';
import { KeyAction, KeyActionId, keyActionType } from './KeyAction';
export class SwitchKeymapAction extends KeyAction {
keymapAbbreviation: string;
constructor(parameter?: SwitchKeymapAction | Keymap) {
super();
if (!parameter) {
return;
}
if (parameter instanceof SwitchKeymapAction) {
this.keymapAbbreviation = parameter.keymapAbbreviation;
} else {
this.keymapAbbreviation = parameter.abbreviation;
}
}
fromJsonObject(jsonObject: any): SwitchKeymapAction {
this.assertKeyActionType(jsonObject);
this.keymapAbbreviation = jsonObject.keymapAbbreviation;
return this;
}
fromBinary(buffer: UhkBuffer): SwitchKeymapAction {
this.readAndAssertKeyActionId(buffer);
this.keymapAbbreviation = buffer.readString();
return this;
}
toJsonObject(): any {
return {
keyActionType: keyActionType.SwitchKeymapAction,
keymapAbbreviation: this.keymapAbbreviation
};
}
toBinary(buffer: UhkBuffer) {
buffer.writeUInt8(KeyActionId.SwitchKeymapAction);
buffer.writeString(this.keymapAbbreviation);
}
toString(): string {
return `<SwitchKeymapAction keymapAbbreviation="${this.keymapAbbreviation}">`;
}
}

View File

@@ -0,0 +1,59 @@
import { assertEnum } from '../../assert';
import { UhkBuffer } from '../../UhkBuffer';
import { KeyAction, KeyActionId, keyActionType } from './KeyAction';
export enum LayerName {
mod,
fn,
mouse
}
export class SwitchLayerAction extends KeyAction {
isLayerToggleable: boolean;
@assertEnum(LayerName)
layer: LayerName;
constructor(other?: SwitchLayerAction) {
super();
if (!other) {
return;
}
this.isLayerToggleable = other.isLayerToggleable;
this.layer = other.layer;
}
fromJsonObject(jsonObject: any): SwitchLayerAction {
this.assertKeyActionType(jsonObject);
this.layer = LayerName[<string>jsonObject.layer];
this.isLayerToggleable = jsonObject.toggle;
return this;
}
fromBinary(buffer: UhkBuffer): SwitchLayerAction {
this.readAndAssertKeyActionId(buffer);
this.layer = buffer.readUInt8();
this.isLayerToggleable = buffer.readBoolean();
return this;
}
toJsonObject(): any {
return {
keyActionType: keyActionType.SwitchLayerAction,
layer: LayerName[this.layer],
toggle: this.isLayerToggleable
};
}
toBinary(buffer: UhkBuffer) {
buffer.writeUInt8(KeyActionId.SwitchLayerAction);
buffer.writeUInt8(this.layer);
buffer.writeBoolean(this.isLayerToggleable);
}
toString(): string {
return `<SwitchLayerAction layer="${this.layer}" toggle="${this.isLayerToggleable}">`;
}
}

View File

@@ -0,0 +1,89 @@
import { UhkBuffer } from '../../UhkBuffer';
import {
KeyAction,
KeyActionId,
KeystrokeAction,
MouseAction,
PlayMacroAction,
SwitchKeymapAction,
SwitchLayerAction,
keyActionType
} from './index';
import { Keymap } from '../Keymap';
import { Macro } from '../Macro';
export class Helper {
static createKeyAction(source: KeyAction | UhkBuffer | any, macros?: Macro[]): KeyAction {
if (source instanceof KeyAction) {
return Helper.fromKeyAction(source);
} else if (source instanceof UhkBuffer) {
return Helper.fromUhkBuffer(source, macros);
} else {
return Helper.fromJSONObject(source, macros);
}
}
private static fromUhkBuffer(buffer: UhkBuffer, macros: Macro[]): KeyAction {
let keyActionFirstByte = buffer.readUInt8();
buffer.backtrack();
if (keyActionFirstByte >= KeyActionId.KeystrokeAction && keyActionFirstByte < KeyActionId.LastKeystrokeAction) {
return new KeystrokeAction().fromBinary(buffer);
}
switch (keyActionFirstByte) {
case KeyActionId.NoneAction:
buffer.readUInt8(); // Read type just to skip it
return undefined;
case KeyActionId.SwitchLayerAction:
return new SwitchLayerAction().fromBinary(buffer);
case KeyActionId.SwitchKeymapAction:
return new SwitchKeymapAction().fromBinary(buffer);
case KeyActionId.MouseAction:
return new MouseAction().fromBinary(buffer);
case KeyActionId.PlayMacroAction:
return new PlayMacroAction().fromBinary(buffer, macros);
default:
throw `Invalid KeyAction first byte: ${keyActionFirstByte}`;
}
}
private static fromKeyAction(keyAction: KeyAction): KeyAction {
let newKeyAction: KeyAction;
if (keyAction instanceof KeystrokeAction) {
newKeyAction = new KeystrokeAction(keyAction);
} else if (keyAction instanceof SwitchLayerAction) {
newKeyAction = new SwitchLayerAction(keyAction);
} else if (keyAction instanceof SwitchKeymapAction) {
newKeyAction = new SwitchKeymapAction(keyAction);
} else if (keyAction instanceof MouseAction) {
newKeyAction = new MouseAction(keyAction);
} else if (keyAction instanceof PlayMacroAction) {
newKeyAction = new PlayMacroAction(keyAction);
}
return newKeyAction;
}
private static fromJSONObject(keyAction: any, macros: Macro[]): KeyAction {
if (!keyAction) {
return;
}
switch (keyAction.keyActionType) {
case keyActionType.KeystrokeAction:
return new KeystrokeAction().fromJsonObject(keyAction);
case keyActionType.SwitchLayerAction:
return new SwitchLayerAction().fromJsonObject(keyAction);
case keyActionType.SwitchKeymapAction:
return new SwitchKeymapAction().fromJsonObject(keyAction);
case keyActionType.MouseAction:
return new MouseAction().fromJsonObject(keyAction);
case keyActionType.PlayMacroAction:
return new PlayMacroAction().fromJsonObject(keyAction, macros);
default:
throw `Invalid KeyAction.keyActionType: "${keyAction.keyActionType}"`;
}
}
}

View File

@@ -0,0 +1,8 @@
export * from './KeyAction';
export * from './KeystrokeAction';
export * from './MouseAction';
export * from './NoneAction';
export * from './PlayMacroAction';
export * from './SwitchKeymapAction';
export * from './SwitchLayerAction';
export * from './helper';

View File

@@ -0,0 +1,45 @@
import { assertUInt16 } from '../../assert';
import { UhkBuffer } from '../../UhkBuffer';
import { MacroAction, MacroActionId, macroActionType } from './MacroAction';
export class DelayMacroAction extends MacroAction {
@assertUInt16
delay: number;
constructor(other?: DelayMacroAction) {
super();
if (!other) {
return;
}
this.delay = other.delay;
}
fromJsonObject(jsObject: any): DelayMacroAction {
this.assertMacroActionType(jsObject);
this.delay = jsObject.delay;
return this;
}
fromBinary(buffer: UhkBuffer): DelayMacroAction {
this.readAndAssertMacroActionId(buffer);
this.delay = buffer.readUInt16();
return this;
}
toJsonObject(): any {
return {
macroActionType: macroActionType.DelayMacroAction,
delay: this.delay
};
}
toBinary(buffer: UhkBuffer) {
buffer.writeUInt8(MacroActionId.DelayMacroAction);
buffer.writeUInt16(this.delay);
}
toString(): string {
return `<DelayMacroAction delay="${this.delay}">`;
}
}

View File

@@ -0,0 +1,189 @@
import { KeyAction, KeystrokeAction, keyActionType } from '../key-action';
import { DelayMacroAction } from './DelayMacroAction';
import { KeyMacroAction } from './KeyMacroAction';
import { MacroAction, MacroSubAction, macroActionType } from './MacroAction';
import { MouseButtonMacroAction } from './MouseButtonMacroAction';
import { MoveMouseMacroAction } from './MoveMouseMacroAction';
import { ScrollMouseMacroAction } from './ScrollMouseMacroAction';
import { TextMacroAction } from './TextMacroAction';
interface JsObjectEditableMacroAction {
macroActionType: string;
action?: string;
scancode?: number;
modifierMask?: number;
mouseButtonsMask?: number;
x?: number;
y?: number;
delay?: number;
text?: string;
}
export class EditableMacroAction {
macroActionType: string;
action: MacroSubAction;
// Key macro action properties
scancode: number;
modifierMask: number;
// Mouse macro action properties
mouseButtonsMask: number;
moveX: number;
moveY: number;
scrollX: number;
scrollY: number;
// Delay macro action properties
delay: number;
// Text macro action properties
text: string;
constructor(jsObject?: JsObjectEditableMacroAction) {
if (!jsObject) {
return;
}
this.macroActionType = jsObject.macroActionType;
switch (this.macroActionType) {
case macroActionType.KeyMacroAction:
this.action = MacroSubAction[jsObject.action];
this.scancode = jsObject.scancode;
this.modifierMask = jsObject.modifierMask;
break;
case macroActionType.MouseButtonMacroAction:
this.action = MacroSubAction[jsObject.action];
this.mouseButtonsMask = jsObject.mouseButtonsMask;
break;
case macroActionType.MoveMouseMacroAction:
this.moveX = jsObject.x;
this.moveY = jsObject.y;
break;
case macroActionType.ScrollMouseMacroAction:
this.scrollX = jsObject.x;
this.scrollY = jsObject.y;
break;
case macroActionType.TextMacroAction:
this.text = jsObject.text;
break;
case macroActionType.DelayMacroAction:
this.delay = jsObject.delay;
break;
default:
break;
}
}
toJsObject(): any {
return {
macroActionType: this.macroActionType,
action: this.action,
delay: this.delay,
text: this.text,
scancode: this.scancode,
modifierMask: this.modifierMask,
mouseButtonsMask: this.mouseButtonsMask,
mouseMove: {
x: this.moveX,
y: this.moveY
},
mouseScroll: {
x: this.scrollX,
y: this.scrollY
}
};
}
fromKeyAction(keyAction: KeyAction): void {
let data = keyAction.toJsonObject();
this.scancode = data.scancode;
this.modifierMask = data.modifierMask;
}
toKeystrokeAction(): KeystrokeAction {
let data = this.toJsObject();
data.keyActionType = keyActionType.KeystrokeAction;
return <KeystrokeAction>(new KeystrokeAction().fromJsonObject(data));
}
setMouseButtons(buttonStates: boolean[]): void {
let bitmask = 0;
for (let i = 0; i < buttonStates.length; i++) {
bitmask |= Number(buttonStates[i]) << i;
}
this.mouseButtonsMask = bitmask;
}
getMouseButtons(): boolean[] {
let enabledMouseButtons: boolean[] = [];
for (let bitmask = this.mouseButtonsMask; bitmask; bitmask >>>= 1) {
enabledMouseButtons.push(Boolean(bitmask & 1));
}
return enabledMouseButtons;
}
toClass(): MacroAction {
switch (this.macroActionType) {
// Delay action
case macroActionType.DelayMacroAction:
return new DelayMacroAction().fromJsonObject({
macroActionType: this.macroActionType,
delay: this.delay
});
// Text action
case macroActionType.TextMacroAction:
return new TextMacroAction().fromJsonObject({
macroActionType: this.macroActionType,
text: this.text
});
// Keypress action
case macroActionType.KeyMacroAction:
return new KeyMacroAction().fromJsonObject({
macroActionType: this.macroActionType,
action: MacroSubAction[this.action],
scancode: this.scancode,
modifierMask: this.modifierMask
});
// Mouse actions
case macroActionType.MouseButtonMacroAction:
return new MouseButtonMacroAction().fromJsonObject({
macroActionType: this.macroActionType,
action: MacroSubAction[this.action],
mouseButtonsMask: this.mouseButtonsMask
});
case macroActionType.MoveMouseMacroAction:
return new MoveMouseMacroAction().fromJsonObject({
macroActionType: this.macroActionType,
x: this.moveX,
y: this.moveY
});
case macroActionType.ScrollMouseMacroAction:
return new ScrollMouseMacroAction().fromJsonObject({
macroActionType: this.macroActionType,
x: this.scrollX,
y: this.scrollY
});
default:
throw new Error('Macro action type is missing or not implemented.');
}
}
isKeyAction(): boolean {
return this.macroActionType === macroActionType.KeyMacroAction;
}
isMouseButtonAction(): boolean {
return this.macroActionType === macroActionType.MouseButtonMacroAction;
}
isOnlyHoldAction(): boolean {
return this.action === MacroSubAction.hold;
}
isOnlyPressAction(): boolean {
return this.action === MacroSubAction.press;
}
isOnlyReleaseAction(): boolean {
return this.action === MacroSubAction.release;
}
}

View File

@@ -0,0 +1,121 @@
import { assertEnum, assertUInt8 } from '../../assert';
import { UhkBuffer } from '../../UhkBuffer';
import { KeyModifiers } from '../KeyModifiers';
import { MacroAction, MacroActionId, MacroSubAction, macroActionType } from './MacroAction';
const NUM_OF_COMBINATIONS = 3; // Cases: scancode, modifer, both
interface JsObjectKeyMacroAction {
macroActionType: string;
action: string;
scancode?: number;
modifierMask?: number;
}
export class KeyMacroAction extends MacroAction {
@assertEnum(MacroSubAction)
action: MacroSubAction;
@assertUInt8
scancode: number;
@assertUInt8
modifierMask: number;
constructor(other?: KeyMacroAction) {
super();
if (!other) {
return;
}
this.action = other.action;
this.scancode = other.scancode;
this.modifierMask = other.modifierMask;
}
fromJsonObject(jsObject: JsObjectKeyMacroAction): KeyMacroAction {
this.assertMacroActionType(jsObject);
this.action = MacroSubAction[jsObject.action];
this.scancode = jsObject.scancode;
this.modifierMask = jsObject.modifierMask;
return this;
}
fromBinary(buffer: UhkBuffer): KeyMacroAction {
let macroActionId: MacroActionId = this.readAndAssertMacroActionId(buffer);
let keyMacroType: number = macroActionId - MacroActionId.KeyMacroAction;
this.action = Math.floor(keyMacroType / NUM_OF_COMBINATIONS);
keyMacroType %= NUM_OF_COMBINATIONS;
if (keyMacroType % 2 === 0) {
this.scancode = buffer.readUInt8();
}
if (keyMacroType !== 0) {
this.modifierMask = buffer.readUInt8();
}
return this;
}
toJsonObject(): any {
let jsObject: JsObjectKeyMacroAction = {
macroActionType: macroActionType.KeyMacroAction,
action: MacroSubAction[this.action]
};
if (this.hasScancode()) {
jsObject.scancode = this.scancode;
}
if (this.hasModifiers()) {
jsObject.modifierMask = this.modifierMask;
}
return jsObject;
}
toBinary(buffer: UhkBuffer) {
let keyMacroType: number = MacroActionId.KeyMacroAction;
keyMacroType += NUM_OF_COMBINATIONS * this.action;
if (this.hasModifiers()) {
++keyMacroType;
if (this.hasScancode()) {
++keyMacroType;
}
}
buffer.writeUInt8(keyMacroType);
if (this.hasScancode()) {
buffer.writeUInt8(this.scancode);
}
if (this.hasModifiers()) {
buffer.writeUInt8(this.modifierMask);
}
}
toString(): string {
return `<KeyMacroAction action="${this.action}" scancode="${this.scancode}" modifierMask="${this.modifierMask}">`;
}
isModifierActive(modifier: KeyModifiers): boolean {
return (this.modifierMask & modifier) > 0;
}
hasScancode(): boolean {
return !!this.scancode;
}
hasModifiers(): boolean {
return !!this.modifierMask;
}
isHoldAction(): boolean {
return this.action === MacroSubAction.hold;
}
isPressAction(): boolean {
return this.action === MacroSubAction.press;
}
isReleaseAction(): boolean {
return this.action === MacroSubAction.release;
}
}

View File

@@ -0,0 +1,77 @@
import { UhkBuffer } from '../../UhkBuffer';
export enum MacroActionId {
KeyMacroAction = 0,
/*
0 - 8 are reserved for KeyMacroAction
PressKeyMacroAction with scancode: 0
PressKeyMacroAction with modifiers: 1
PressKeyMacroAction with scancode and modifiers 2
HoldKeyMacroAction with scancode: 3
HoldKeyMacroAction with modifiers: 4
HoldKeyMacroAction with scancode and modifiers 5
ReleaseKeyMacroAction with scancode: 6
ReleaseKeyMacroAction with modifiers: 7
ReleaseKeyMacroAction with scancode and modifiers 8
*/
LastKeyMacroAction = 8,
MouseButtonMacroAction = 9,
/*
9 - 11 are reserved for MouseButtonMacroAction
PressMouseButtonsMacroAction = 9,
HoldMouseButtonsMacroAction = 10,
ReleaseMouseButtonsMacroAction = 11,
*/
LastMouseButtonMacroAction = 11,
MoveMouseMacroAction = 12,
ScrollMouseMacroAction = 13,
DelayMacroAction = 14,
TextMacroAction = 15
}
export enum MacroSubAction {
press = 0,
hold = 1,
release = 2
}
export let macroActionType = {
KeyMacroAction : 'key',
MouseButtonMacroAction : 'mouseButton',
MoveMouseMacroAction : 'moveMouse',
ScrollMouseMacroAction : 'scrollMouse',
DelayMacroAction : 'delay',
TextMacroAction : 'text'
};
export abstract class MacroAction {
assertMacroActionType(jsObject: any) {
let macroActionClassname = this.constructor.name;
let macroActionTypeString = macroActionType[macroActionClassname];
if (jsObject.macroActionType !== macroActionTypeString) {
throw `Invalid ${macroActionClassname}.macroActionType: ${jsObject.macroActionType}`;
}
}
readAndAssertMacroActionId(buffer: UhkBuffer): MacroActionId {
let classname: string = this.constructor.name;
let readMacroActionId: MacroActionId = buffer.readUInt8();
let macroActionId: MacroActionId = MacroActionId[classname];
if (macroActionId === MacroActionId.KeyMacroAction) {
if (readMacroActionId < MacroActionId.KeyMacroAction || readMacroActionId > MacroActionId.LastKeyMacroAction) {
throw `Invalid ${classname} first byte: ${readMacroActionId}`;
}
} else if (macroActionId === MacroActionId.MouseButtonMacroAction) {
if (readMacroActionId < MacroActionId.MouseButtonMacroAction ||
readMacroActionId > MacroActionId.LastMouseButtonMacroAction) {
throw `Invalid ${classname} first byte: ${readMacroActionId}`;
}
} else if (readMacroActionId !== macroActionId) {
throw `Invalid ${classname} first byte: ${readMacroActionId}`;
}
return readMacroActionId;
}
abstract toJsonObject(): any;
abstract toBinary(buffer: UhkBuffer): void;
}

View File

@@ -0,0 +1,95 @@
import { assertEnum, assertUInt8 } from '../../assert';
import { UhkBuffer } from '../../UhkBuffer';
import { MacroAction, MacroActionId, MacroSubAction, macroActionType } from './MacroAction';
export enum MouseButtons {
Left = 1 << 0,
Middle = 1 << 1,
Right = 1 << 2
};
interface JsObjectMouseButtonMacroAction {
macroActionType: string;
action: string;
mouseButtonsMask?: number;
}
export class MouseButtonMacroAction extends MacroAction {
@assertEnum(MacroSubAction)
action: MacroSubAction;
@assertUInt8
mouseButtonsMask: number;
constructor(other?: MouseButtonMacroAction) {
super();
if (!other) {
return;
}
this.action = other.action;
this.mouseButtonsMask = other.mouseButtonsMask;
}
fromJsonObject(jsObject: JsObjectMouseButtonMacroAction): MouseButtonMacroAction {
this.assertMacroActionType(jsObject);
this.action = MacroSubAction[jsObject.action];
this.mouseButtonsMask = jsObject.mouseButtonsMask;
return this;
}
fromBinary(buffer: UhkBuffer): MouseButtonMacroAction {
let macroActionId: MacroActionId = this.readAndAssertMacroActionId(buffer);
this.action = macroActionId - MacroActionId.MouseButtonMacroAction;
this.mouseButtonsMask = buffer.readUInt8();
return this;
}
toJsonObject(): any {
return {
macroActionType: macroActionType.MouseButtonMacroAction,
action: MacroSubAction[this.action],
mouseButtonsMask: this.mouseButtonsMask
};
}
toBinary(buffer: UhkBuffer): void {
buffer.writeUInt8(MacroActionId.MouseButtonMacroAction + this.action);
buffer.writeUInt8(this.mouseButtonsMask);
}
setMouseButtons(buttonStates: boolean[]): void {
let bitmask = 0;
for (let i = 0; i < buttonStates.length; i++) {
bitmask |= Number(buttonStates[i]) << i;
}
this.mouseButtonsMask = bitmask;
}
getMouseButtons(): boolean[] {
let enabledMouseButtons: boolean[] = [];
for (let bitmask = this.mouseButtonsMask; bitmask; bitmask >>>= 1) {
enabledMouseButtons.push(Boolean(bitmask & 1));
}
return enabledMouseButtons;
}
toString(): string {
return `<MouseButtonMacroAction mouseButtonsMask="${this.mouseButtonsMask}">`;
}
hasButtons(): boolean {
return this.mouseButtonsMask !== 0;
}
isOnlyHoldAction(): boolean {
return this.action === MacroSubAction.hold;
}
isOnlyPressAction(): boolean {
return this.action === MacroSubAction.press;
}
isOnlyReleaseAction(): boolean {
return this.action === MacroSubAction.release;
}
}

View File

@@ -0,0 +1,53 @@
import { assertInt16 } from '../../assert';
import { UhkBuffer } from '../../UhkBuffer';
import { MacroAction, MacroActionId, macroActionType } from './MacroAction';
export class MoveMouseMacroAction extends MacroAction {
@assertInt16
x: number;
@assertInt16
y: number;
constructor(other?: MoveMouseMacroAction) {
super();
if (!other) {
return;
}
this.x = other.x;
this.y = other.y;
}
fromJsonObject(jsObject: any): MoveMouseMacroAction {
this.assertMacroActionType(jsObject);
this.x = jsObject.x;
this.y = jsObject.y;
return this;
}
fromBinary(buffer: UhkBuffer): MoveMouseMacroAction {
this.readAndAssertMacroActionId(buffer);
this.x = buffer.readInt16();
this.y = buffer.readInt16();
return this;
}
toJsonObject(): any {
return {
macroActionType: macroActionType.MoveMouseMacroAction,
x: this.x,
y: this.y
};
}
toBinary(buffer: UhkBuffer) {
buffer.writeUInt8(MacroActionId.MoveMouseMacroAction);
buffer.writeInt16(this.x);
buffer.writeInt16(this.y);
}
toString(): string {
return `<MoveMouseMacroAction pos="(${this.x},${this.y})">`;
}
}

View File

@@ -0,0 +1,53 @@
import { assertInt16 } from '../../assert';
import { UhkBuffer } from '../../UhkBuffer';
import { MacroAction, MacroActionId, macroActionType } from './MacroAction';
export class ScrollMouseMacroAction extends MacroAction {
@assertInt16
x: number;
@assertInt16
y: number;
constructor(other?: ScrollMouseMacroAction) {
super();
if (!other) {
return;
}
this.x = other.x;
this.y = other.y;
}
fromJsonObject(jsObject: any): ScrollMouseMacroAction {
this.assertMacroActionType(jsObject);
this.x = jsObject.x;
this.y = jsObject.y;
return this;
}
fromBinary(buffer: UhkBuffer): ScrollMouseMacroAction {
this.readAndAssertMacroActionId(buffer);
this.x = buffer.readInt16();
this.y = buffer.readInt16();
return this;
}
toJsonObject(): any {
return {
macroActionType: macroActionType.ScrollMouseMacroAction,
x: this.x,
y: this.y
};
}
toBinary(buffer: UhkBuffer) {
buffer.writeUInt8(MacroActionId.ScrollMouseMacroAction);
buffer.writeInt16(this.x);
buffer.writeInt16(this.y);
}
toString(): string {
return `<ScrollMouseMacroAction pos="(${this.x},${this.y})">`;
}
}

View File

@@ -0,0 +1,43 @@
import { UhkBuffer } from '../../UhkBuffer';
import { MacroAction, MacroActionId, macroActionType } from './MacroAction';
export class TextMacroAction extends MacroAction {
text: string;
constructor(other?: TextMacroAction) {
super();
if (!other) {
return;
}
this.text = other.text;
}
fromJsonObject(jsObject: any): TextMacroAction {
this.assertMacroActionType(jsObject);
this.text = jsObject.text;
return this;
}
fromBinary(buffer: UhkBuffer): TextMacroAction {
this.readAndAssertMacroActionId(buffer);
this.text = buffer.readString();
return this;
}
toJsonObject(): any {
return {
macroActionType: macroActionType.TextMacroAction,
text: this.text
};
}
toBinary(buffer: UhkBuffer) {
buffer.writeUInt8(MacroActionId.TextMacroAction);
buffer.writeString(this.text);
}
toString(): string {
return `<TextMacroAction text="${this.text}">`;
}
}

View File

@@ -0,0 +1,88 @@
import { UhkBuffer } from '../../UhkBuffer';
import {
DelayMacroAction,
KeyMacroAction,
MacroAction,
MacroActionId,
MouseButtonMacroAction,
MoveMouseMacroAction,
ScrollMouseMacroAction,
TextMacroAction,
macroActionType
} from './index';
export class Helper {
static createMacroAction(source: MacroAction | UhkBuffer | any): MacroAction {
if (source instanceof MacroAction) {
return Helper.fromMacroAction(source);
} else if (source instanceof UhkBuffer) {
return Helper.fromUhkBuffer(source);
} else {
return Helper.fromJSONObject(source);
}
}
private static fromUhkBuffer(buffer: UhkBuffer): MacroAction {
let macroActionFirstByte = buffer.readUInt8();
buffer.backtrack();
if (macroActionFirstByte >= MacroActionId.KeyMacroAction && macroActionFirstByte <= MacroActionId.LastKeyMacroAction) {
return new KeyMacroAction().fromBinary(buffer);
} else if (
macroActionFirstByte >= MacroActionId.MouseButtonMacroAction &&
macroActionFirstByte <= MacroActionId.LastMouseButtonMacroAction
) {
return new MouseButtonMacroAction().fromBinary(buffer);
}
switch (macroActionFirstByte) {
case MacroActionId.MoveMouseMacroAction:
return new MoveMouseMacroAction().fromBinary(buffer);
case MacroActionId.ScrollMouseMacroAction:
return new ScrollMouseMacroAction().fromBinary(buffer);
case MacroActionId.DelayMacroAction:
return new DelayMacroAction().fromBinary(buffer);
case MacroActionId.TextMacroAction:
return new TextMacroAction().fromBinary(buffer);
default:
throw `Invalid MacroAction first byte: ${macroActionFirstByte}`;
}
}
private static fromMacroAction(macroAction: MacroAction): MacroAction {
let newMacroAction: MacroAction;
if (macroAction instanceof KeyMacroAction) {
newMacroAction = new KeyMacroAction(macroAction);
} else if (macroAction instanceof MouseButtonMacroAction) {
newMacroAction = new MouseButtonMacroAction(macroAction);
} else if (macroAction instanceof MoveMouseMacroAction) {
newMacroAction = new MoveMouseMacroAction(macroAction);
} else if (macroAction instanceof ScrollMouseMacroAction) {
newMacroAction = new ScrollMouseMacroAction(macroAction);
} else if (macroAction instanceof DelayMacroAction) {
newMacroAction = new DelayMacroAction(macroAction);
} else if (macroAction instanceof TextMacroAction) {
newMacroAction = new TextMacroAction(macroAction);
}
return newMacroAction;
}
private static fromJSONObject(macroAction: any): MacroAction {
switch (macroAction.macroActionType) {
case macroActionType.KeyMacroAction:
return new KeyMacroAction().fromJsonObject(macroAction);
case macroActionType.MouseButtonMacroAction:
return new MouseButtonMacroAction().fromJsonObject(macroAction);
case macroActionType.MoveMouseMacroAction:
return new MoveMouseMacroAction().fromJsonObject(macroAction);
case macroActionType.ScrollMouseMacroAction:
return new ScrollMouseMacroAction().fromJsonObject(macroAction);
case macroActionType.DelayMacroAction:
return new DelayMacroAction().fromJsonObject(macroAction);
case macroActionType.TextMacroAction:
return new TextMacroAction().fromJsonObject(macroAction);
default:
throw `Invalid MacroAction.macroActionType: "${macroAction.macroActionType}"`;
}
}
}

View File

@@ -0,0 +1,9 @@
export * from './DelayMacroAction';
export * from './EditableMacroAction';
export * from './KeyMacroAction';
export * from './MacroAction';
export * from './MoveMouseMacroAction';
export * from './MouseButtonMacroAction';
export * from './ScrollMouseMacroAction';
export * from './TextMacroAction';
export * from './helper';

View File

@@ -0,0 +1,156 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "UHK Configuration",
"description": "UHK Configuration",
"type": "object",
"properties": {
"prologue": {
"description": "Prologue",
"type": "integer"
},
"keymaps": {
"description": "Array of keymaps",
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"description": "Id of the keymap",
"type": "integer"
},
"isDefault": {
"type": "boolean"
},
"layers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"modules": {
"type": "array",
"items": {
"type": "object",
"properties": {
"keyActions": {
"type": "array",
"items": {
"type": [
"null",
"object"
],
"properties": {
"keyActionType": {
"type": "string",
"enum": [
"keystroke",
"switchLayer",
"switchKeymap",
"playMacro",
"mouse"
]
}
},
"required": [
"keyActionType"
],
"anyOf": [
{
"properties": {
"keyActionType": {
"type": "string",
"enum": [
"mouse"
]
},
"mouseAction": {
"type": "string",
"enum": [
"leftClick",
"middleClick",
"rightClick",
"moveUp",
"moveDown",
"moveLeft",
"moveRight",
"scrollUp",
"scrollDown",
"scrollLeft",
"scrollRight",
"accelerate",
"decelerate"
]
}
}
},
{
"type": "object",
"properties": {
"keyActionType": {
"type": "string",
"enum": [
"keystroke"
]
},
"scancode": {
"type": "integer"
}
}
},
{
"type": "object",
"properties": {
"keyActionType": {
"type": "string",
"enum": [
"switchLayer"
]
},
"layer": {
"type": "string"
},
"toggle": {
"type": "boolean"
}
}
},
{
"type": "object",
"properties": {
"keyActionType": {
"type": "string",
"enum": [
"switchKeymap"
]
},
"keymapId": {
"type": "integer"
}
}
},
{
"properties": {
"keyActionType": {
"type": "string",
"enum": [
"playMacro"
]
},
"macroIndex": {
"type": "integer"
}
}
}
]
}
}
}
}
}
}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,8 @@
{
"signature": "UHK",
"dataModelVersion": 0,
"hardwareId": 0,
"brandId": 0,
"isIso": false,
"hasBacklighting": false
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff