feat(config): Read / write hardware configuration area (#423)
* add write-hca.js * refactor: Move config serializer into the uhk-common package * refactor: Move getTransferBuffers into the uhk-usb package * refactor: delete obsoleted classes * build: add uhk-usb build command * refactor: move eeprom transfer to uhk-usb package * fix: Fix write-hca.js * feat: load hardware config from the device and * style: fix ts lint errors * build: fix rxjs dependency resolve * test: Add jasmine unit test framework to the tet serializer * fix(user-config): A "type": "basic", properties to the "keystroke" action types * feat(usb): set chmod+x on write-hca.js * feat(usb): Create USB logger * style: Fix type * build: Add chalk to dependencies. Chalk will colorize the output
This commit is contained in:
committed by
László Monda
parent
1122784bdb
commit
9294bede50
@@ -32,7 +32,6 @@ install:
|
||||
|
||||
before_script:
|
||||
- npm run build
|
||||
- npm run build:test
|
||||
- npm run lint
|
||||
|
||||
script:
|
||||
|
||||
22
package-lock.json
generated
22
package-lock.json
generated
@@ -16,13 +16,19 @@
|
||||
"integrity": "sha1-ixuKArfpLRDOibzRbnroza5xKZ8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "8.0.25"
|
||||
"@types/node": "8.0.30"
|
||||
}
|
||||
},
|
||||
"@types/jasmine": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.6.0.tgz",
|
||||
"integrity": "sha512-1ZZdFvYA5zARDXPj5+VF0bwDZWH/o0QQWJVDc5srdC/DngcCZXskR33eR/4PielGvBjLQpQOd6KiQbmtqVkeZA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "8.0.25",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.25.tgz",
|
||||
"integrity": "sha512-zT+t9841g1HsjLtPMCYxmb1U4pcZ2TOegAKiomlmj6bIziuaEYHUavxLE9NRwdntY0vOCrgHho6OXjDX7fm/Kw==",
|
||||
"version": "8.0.30",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.30.tgz",
|
||||
"integrity": "sha512-IaQtG3DWe9gRsmk1DqNnYyRVjGDVcBdZywkRVF2f62Boe8XKmlR7lNcwC6pk4V4W8nk+Zu+vdGMsOdRTDj1JPA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node-hid": {
|
||||
@@ -37,7 +43,7 @@
|
||||
"integrity": "sha1-+9YF06q7WccUFqj1ku7krinppuE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "8.0.25"
|
||||
"@types/node": "8.0.30"
|
||||
}
|
||||
},
|
||||
"7zip": {
|
||||
@@ -8816,9 +8822,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.4.2.tgz",
|
||||
"integrity": "sha1-+DlfhdRZJ2BnyYiqQYN6j4KHCEQ=",
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.2.tgz",
|
||||
"integrity": "sha1-A4qV99m7tCCxvzW6MdTFwd0//jQ=",
|
||||
"dev": true
|
||||
},
|
||||
"uglify-js": {
|
||||
|
||||
15
package.json
15
package.json
@@ -16,7 +16,8 @@
|
||||
"devDependencies": {
|
||||
"@types/electron-devtools-installer": "^2.0.2",
|
||||
"@types/electron-settings": "^3.0.0",
|
||||
"@types/node": "~8.0.25",
|
||||
"@types/jasmine": "2.6.0",
|
||||
"@types/node": "8.0.30",
|
||||
"@types/node-hid": "^0.5.2",
|
||||
"@types/usb": "^1.1.3",
|
||||
"autoprefixer": "^6.5.3",
|
||||
@@ -44,25 +45,27 @@
|
||||
"ts-loader": "^2.3.1",
|
||||
"ts-node": "~3.0.4",
|
||||
"tslint": "~5.5.0",
|
||||
"typescript": "^2.4.2",
|
||||
"typescript": "2.5.2",
|
||||
"webpack": "^2.4.1"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "lerna bootstrap",
|
||||
"test": "lerna exec --scope test-serializer npm test",
|
||||
"lint": "run-s -scn lint:ts lint:style",
|
||||
"lint:ts": "run-p -sn lint:ts:electron-main lint:ts:electron-renderer lint:ts:web lint:ts:test-serializer",
|
||||
"lint:ts": "run-p -sn lint:ts:electron-main lint:ts:electron-renderer lint:ts:web lint:ts:test-serializer lint:ts:uhk-usb",
|
||||
"lint:ts:electron-main": "tslint --type-check --project ./packages/uhk-agent/tsconfig.json",
|
||||
"lint:ts:electron-renderer": "tslint --type-check --project ./packages/uhk-web/src/tsconfig.renderer.json",
|
||||
"lint:ts:web": "tslint --type-check --project ./packages/uhk-web/src/tsconfig.app.json",
|
||||
"lint:ts:test-serializer": "tslint --type-check --project ./packages/test-serializer/tsconfig.json",
|
||||
"lint:ts:uhk-usb": "tslint --type-check --project ./packages/uhk-usb/tsconfig.json",
|
||||
"lint:style": "stylelint \"packages/uhk-agent/src/**/*.scss\" \"packages/uhk-web/src/**/*.scss\" --syntax scss",
|
||||
"build": "run-s build:web build:electron",
|
||||
"build": "run-s build:web build:electron build:usb build:common",
|
||||
"build:web": "lerna exec --scope uhk-web npm run build",
|
||||
"build:electron": "cross-env AOT_BUILD=true run-s -sn build:electron:renderer build:electron:main",
|
||||
"build:electron:main": "lerna exec --scope uhk-agent npm run build",
|
||||
"build:electron:renderer": "lerna exec --scope uhk-web npm run build:renderer",
|
||||
"build:test": "lerna exec --scope test-serializer npm run build",
|
||||
"build:common": "lerna exec --scope uhk-common npm run build",
|
||||
"build:usb": "lerna exec --scope uhk-usb npm run build",
|
||||
"server:web": "lerna exec --scope uhk-web npm start",
|
||||
"server:electron": "lerna exec --scope uhk-web npm run server:renderer",
|
||||
"electron": "lerna exec --scope uhk-agent npm start",
|
||||
@@ -70,6 +73,6 @@
|
||||
"pack": "node ./scripts/release.js",
|
||||
"sprites": "node ./scripts/generate-svg-sprites",
|
||||
"release": "node ./scripts/release.js",
|
||||
"clean": "lerna exec rimraf ./node_modules ./dist && rimraf ./node_modules ./dist package-lock.json"
|
||||
"clean": "lerna exec rimraf ./node_modules ./dist && rimraf ./node_modules ./dist"
|
||||
}
|
||||
}
|
||||
|
||||
7
packages/test-serializer/.gitignore
vendored
7
packages/test-serializer/.gitignore
vendored
@@ -1,4 +1,3 @@
|
||||
uhk-config.bin
|
||||
uhk-config-serialized.json
|
||||
uhk-config-serialized.bin
|
||||
test-serializer.js
|
||||
user-config.bin
|
||||
user-config-serialized.json
|
||||
user-config-serialized.bin
|
||||
|
||||
8
packages/test-serializer/jasmine.json
Normal file
8
packages/test-serializer/jasmine.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"spec_dir": "spec",
|
||||
"spec_files": [
|
||||
"**/*[sS]pec.ts"
|
||||
],
|
||||
"stopSpecOnExpectationFailure": true,
|
||||
"random": false
|
||||
}
|
||||
1029
packages/test-serializer/package-lock.json
generated
Normal file
1029
packages/test-serializer/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -16,10 +16,16 @@
|
||||
"dependencies": {
|
||||
},
|
||||
"devDependencies": {
|
||||
"uhk-web": "^1.0.0"
|
||||
"@types/jasmine": "^2.6.0",
|
||||
"@types/node": "8.0.30",
|
||||
"jasmine": "^2.8.0",
|
||||
"jasmine-core": "^2.8.0",
|
||||
"jasmine-node": "2.0.0",
|
||||
"jasmine-ts": "^0.2.1",
|
||||
"ts-node": "3.3.0",
|
||||
"uhk-common": "1.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack",
|
||||
"test": "node ./test-serializer.js"
|
||||
"test": "jasmine-ts --config=jasmine.json"
|
||||
}
|
||||
}
|
||||
|
||||
33
packages/test-serializer/spec/test-serializer.spec.ts
Normal file
33
packages/test-serializer/spec/test-serializer.spec.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { UhkBuffer, UserConfiguration } from '../../uhk-common/index';
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
const userConfig = JSON.parse(fs.readFileSync('../uhk-web/src/app/services/user-config.json'));
|
||||
|
||||
describe('Test Serializer', () => {
|
||||
it('full config match', () => {
|
||||
const config1Js = userConfig;
|
||||
const config1Ts: UserConfiguration = new UserConfiguration().fromJsonObject(config1Js);
|
||||
const config1Buffer = new UhkBuffer();
|
||||
config1Ts.toBinary(config1Buffer);
|
||||
const config1BufferContent = config1Buffer.getBufferContent();
|
||||
fs.writeFileSync('user-config.bin', config1BufferContent);
|
||||
|
||||
config1Buffer.offset = 0;
|
||||
console.log();
|
||||
const config2Ts = new UserConfiguration().fromBinary(config1Buffer);
|
||||
console.log('\n');
|
||||
const config2Js = config2Ts.toJsonObject();
|
||||
const config2Buffer = new UhkBuffer();
|
||||
config2Ts.toBinary(config2Buffer);
|
||||
fs.writeFileSync('user-config-serialized.json', JSON.stringify(config2Js, undefined, 4));
|
||||
const config2BufferContent = config1Buffer.getBufferContent();
|
||||
fs.writeFileSync('user-config-serialized.bin', config2BufferContent);
|
||||
|
||||
expect(config1Js).toEqual(config2Js);
|
||||
|
||||
const buffersContentsAreEqual: boolean = Buffer.compare(config1BufferContent, config2BufferContent) === 0;
|
||||
expect(buffersContentsAreEqual).toBe(true);
|
||||
|
||||
});
|
||||
});
|
||||
@@ -1,45 +0,0 @@
|
||||
import { UserConfiguration } from '../uhk-web/src/app/config-serializer/config-items/user-configuration';
|
||||
import { UhkBuffer } from '../uhk-web/src/app/config-serializer/uhk-buffer';
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
const userConfig = JSON.parse(fs.readFileSync('../uhk-web/src/app/config-serializer/user-config.json'));
|
||||
|
||||
const config1Js = userConfig;
|
||||
const config1Ts: UserConfiguration = new UserConfiguration().fromJsonObject(config1Js);
|
||||
const config1Buffer = new UhkBuffer();
|
||||
config1Ts.toBinary(config1Buffer);
|
||||
const config1BufferContent = config1Buffer.getBufferContent();
|
||||
fs.writeFileSync('user-config.bin', config1BufferContent);
|
||||
|
||||
config1Buffer.offset = 0;
|
||||
console.log();
|
||||
const config2Ts = new UserConfiguration().fromBinary(config1Buffer);
|
||||
console.log('\n');
|
||||
const config2Js = config2Ts.toJsonObject();
|
||||
const config2Buffer = new UhkBuffer();
|
||||
config2Ts.toBinary(config2Buffer);
|
||||
fs.writeFileSync('user-config-serialized.json', JSON.stringify(config2Js, undefined, 4));
|
||||
const config2BufferContent = config1Buffer.getBufferContent();
|
||||
fs.writeFileSync('user-config-serialized.bin', config2BufferContent);
|
||||
|
||||
console.log('\n');
|
||||
let returnValue = 0;
|
||||
try {
|
||||
assert.deepEqual(config1Js, config2Js);
|
||||
console.log('JSON configurations are identical.');
|
||||
} catch (error) {
|
||||
console.log('JSON configurations differ.');
|
||||
returnValue = 1;
|
||||
}
|
||||
|
||||
const buffersContentsAreEqual: boolean = Buffer.compare(config1BufferContent, config2BufferContent) === 0;
|
||||
if (buffersContentsAreEqual) {
|
||||
console.log('Binary configurations are identical.');
|
||||
} else {
|
||||
console.log('Binary configurations differ.');
|
||||
returnValue += 2;
|
||||
}
|
||||
|
||||
process.exit(returnValue);
|
||||
@@ -5,10 +5,7 @@
|
||||
"moduleResolution": "node",
|
||||
"experimentalDecorators": true,
|
||||
"typeRoots": [
|
||||
"../node_modules/@types"
|
||||
],
|
||||
"types": [
|
||||
"node"
|
||||
"./node_modules/@types"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
12
packages/uhk-agent/package-lock.json
generated
12
packages/uhk-agent/package-lock.json
generated
@@ -392,9 +392,9 @@
|
||||
}
|
||||
},
|
||||
"electron": {
|
||||
"version": "1.7.6",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-1.7.6.tgz",
|
||||
"integrity": "sha1-+2nqMb0D3w7/JH8m8LU4vSm27nI=",
|
||||
"version": "1.7.5",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-1.7.5.tgz",
|
||||
"integrity": "sha1-BloxAr+LhxAt9QxQmF/v5sVpBFs=",
|
||||
"requires": {
|
||||
"@types/node": "7.0.43",
|
||||
"electron-download": "3.3.0",
|
||||
@@ -432,9 +432,9 @@
|
||||
"integrity": "sha1-ihBD4ys6HaHD9VPc4oznZCRhZ+M="
|
||||
},
|
||||
"electron-log": {
|
||||
"version": "2.2.9",
|
||||
"resolved": "https://registry.npmjs.org/electron-log/-/electron-log-2.2.9.tgz",
|
||||
"integrity": "sha512-WNMSipQYurNxY14RO6IKgcxcZg1e4aNVpUUJK9q7Bqe0TZEKn1e5h4HiQKhTgVLqKrUn++ugOZrty450P9vpjA=="
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/electron-log/-/electron-log-2.2.6.tgz",
|
||||
"integrity": "sha1-zPo+CbOfMhRoyQpkJwE4CjQHnxo="
|
||||
},
|
||||
"electron-rebuild": {
|
||||
"version": "1.6.0",
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
"electron-settings": "3.0.14",
|
||||
"electron-updater": "2.2.0",
|
||||
"node-hid": "0.5.4",
|
||||
"sudo-prompt": "^7.0.0"
|
||||
"sudo-prompt": "^7.0.0",
|
||||
"uhk-usb": "1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"uhk-common": "^1.0.0"
|
||||
|
||||
@@ -8,6 +8,7 @@ import { autoUpdater } from 'electron-updater';
|
||||
import * as path from 'path';
|
||||
import * as url from 'url';
|
||||
import * as commandLineArgs from 'command-line-args';
|
||||
import { UhkHidDevice } from 'uhk-usb';
|
||||
|
||||
// import { ElectronDataStorageRepositoryService } from './services/electron-datastorage-repository.service';
|
||||
import { CommandLineArgs } from 'uhk-common';
|
||||
@@ -16,7 +17,6 @@ import { logger } from './services/logger.service';
|
||||
import { AppUpdateService } from './services/app-update.service';
|
||||
import { AppService } from './services/app.service';
|
||||
import { SudoService } from './services/sudo.service';
|
||||
import { UhkHidDeviceService } from './services/uhk-hid-device.service';
|
||||
|
||||
const optionDefinitions = [
|
||||
{ name: 'addons', type: Boolean, defaultOption: false }
|
||||
@@ -33,7 +33,7 @@ let win: Electron.BrowserWindow;
|
||||
autoUpdater.logger = logger;
|
||||
|
||||
let deviceService: DeviceService;
|
||||
let uhkHidDeviceService: UhkHidDeviceService;
|
||||
let uhkHidDeviceService: UhkHidDevice;
|
||||
let appUpdateService: AppUpdateService;
|
||||
let appService: AppService;
|
||||
let sudoService: SudoService;
|
||||
@@ -51,7 +51,7 @@ function createWindow() {
|
||||
});
|
||||
win.setMenuBarVisibility(false);
|
||||
win.maximize();
|
||||
uhkHidDeviceService = new UhkHidDeviceService(logger);
|
||||
uhkHidDeviceService = new UhkHidDevice(logger);
|
||||
deviceService = new DeviceService(logger, win, uhkHidDeviceService);
|
||||
appUpdateService = new AppUpdateService(logger, win, app);
|
||||
appService = new AppService(logger, win, deviceService, options, uhkHidDeviceService);
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { ipcMain, BrowserWindow } from 'electron';
|
||||
import { UhkHidDevice } from 'uhk-usb';
|
||||
|
||||
import { CommandLineArgs, IpcEvents, AppStartInfo, LogService } from 'uhk-common';
|
||||
import { MainServiceBase } from './main-service-base';
|
||||
import { DeviceService } from './device.service';
|
||||
import { UhkHidDeviceService } from './uhk-hid-device.service';
|
||||
|
||||
export class AppService extends MainServiceBase {
|
||||
constructor(protected logService: LogService,
|
||||
protected win: Electron.BrowserWindow,
|
||||
private deviceService: DeviceService,
|
||||
private options: CommandLineArgs,
|
||||
private uhkHidDeviceService: UhkHidDeviceService) {
|
||||
private uhkHidDeviceService: UhkHidDevice) {
|
||||
super(logService, win);
|
||||
|
||||
ipcMain.on(IpcEvents.app.getAppStartInfo, this.handleAppStartInfo.bind(this));
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ipcMain } from 'electron';
|
||||
|
||||
import { Constants, IpcEvents, LogService, IpcResponse } from 'uhk-common';
|
||||
|
||||
import { IpcEvents, LogService, IpcResponse, ConfigurationReply } from 'uhk-common';
|
||||
import { Constants, EepromTransfer, SystemPropertyIds, UsbCommand } from 'uhk-usb';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { Device, devices } from 'node-hid';
|
||||
import { UhkHidDevice } from 'uhk-usb';
|
||||
|
||||
import 'rxjs/add/observable/interval';
|
||||
import 'rxjs/add/operator/startWith';
|
||||
@@ -12,38 +12,6 @@ import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/do';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
|
||||
import { UhkHidDeviceService } from './uhk-hid-device.service';
|
||||
|
||||
/**
|
||||
* UHK USB Communications command. All communication package should have start with a command code.
|
||||
*/
|
||||
enum Command {
|
||||
GetProperty = 0,
|
||||
UploadConfig = 8,
|
||||
ApplyConfig = 9,
|
||||
LaunchEepromTransfer = 12,
|
||||
ReadUserConfig = 15,
|
||||
GetKeyboardState = 16
|
||||
}
|
||||
|
||||
enum EepromTransfer {
|
||||
ReadHardwareConfig = 0,
|
||||
WriteHardwareConfig = 1,
|
||||
ReadUserConfig = 2,
|
||||
WriteUserConfig = 3
|
||||
}
|
||||
|
||||
enum SystemPropertyIds {
|
||||
UsbProtocolVersion = 0,
|
||||
BridgeProtocolVersion = 1,
|
||||
DataModelVersion = 2,
|
||||
FirmwareVersion = 3,
|
||||
HardwareConfigSize = 4,
|
||||
UserConfigSize = 5
|
||||
}
|
||||
|
||||
const snooze = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
/**
|
||||
* IpcMain pair of the UHK Communication
|
||||
* Functionality:
|
||||
@@ -57,10 +25,10 @@ export class DeviceService {
|
||||
|
||||
constructor(private logService: LogService,
|
||||
private win: Electron.BrowserWindow,
|
||||
private device: UhkHidDeviceService) {
|
||||
private device: UhkHidDevice) {
|
||||
this.pollUhkDevice();
|
||||
ipcMain.on(IpcEvents.device.saveUserConfiguration, this.saveUserConfiguration.bind(this));
|
||||
ipcMain.on(IpcEvents.device.loadUserConfiguration, this.loadUserConfiguration.bind(this));
|
||||
ipcMain.on(IpcEvents.device.loadConfigurations, this.loadConfigurations.bind(this));
|
||||
logService.debug('[DeviceService] init success');
|
||||
}
|
||||
|
||||
@@ -76,32 +44,64 @@ export class DeviceService {
|
||||
* Return with the actual UserConfiguration from UHK Device
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
public async loadUserConfiguration(event: Electron.Event): Promise<void> {
|
||||
public async loadConfigurations(event: Electron.Event): Promise<void> {
|
||||
try {
|
||||
const userConfiguration = await this.loadConfiguration(
|
||||
SystemPropertyIds.UserConfigSize,
|
||||
UsbCommand.ReadUserConfig,
|
||||
'user configuration');
|
||||
|
||||
const hardwareConfiguration = await this.loadConfiguration(
|
||||
SystemPropertyIds.HardwareConfigSize,
|
||||
UsbCommand.ReadHardwareConfig,
|
||||
'hardware configuration');
|
||||
|
||||
const response: ConfigurationReply = {
|
||||
success: true,
|
||||
userConfiguration,
|
||||
hardwareConfiguration
|
||||
};
|
||||
event.sender.send(IpcEvents.device.loadConfigurationReply, JSON.stringify(response));
|
||||
} catch (error) {
|
||||
const response: ConfigurationReply = {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
event.sender.send(IpcEvents.device.loadConfigurationReply, JSON.stringify(response));
|
||||
} finally {
|
||||
this.device.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return with the actual user / hardware fonfiguration from UHK Device
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
public async loadConfiguration(property: SystemPropertyIds, config: UsbCommand, configName: string): Promise<string> {
|
||||
let response = [];
|
||||
|
||||
try {
|
||||
this.logService.debug('[DeviceService] USB[T]: Read user configuration size from keyboard');
|
||||
const configSize = await this.getUserConfigSizeFromKeyboard();
|
||||
this.logService.debug(`[DeviceService] USB[T]: Read ${configName} size from keyboard`);
|
||||
const configSize = await this.getConfigSizeFromKeyboard(property);
|
||||
const chunkSize = 63;
|
||||
let offset = 0;
|
||||
let configBuffer = new Buffer(0);
|
||||
|
||||
this.logService.debug('[DeviceService] USB[T]: Read user configuration from keyboard');
|
||||
this.logService.debug(`[DeviceService] USB[T]: Read ${configName} from keyboard`);
|
||||
while (offset < configSize) {
|
||||
const chunkSizeToRead = Math.min(chunkSize, configSize - offset);
|
||||
const writeBuffer = Buffer.from([Command.ReadUserConfig, chunkSizeToRead, offset & 0xff, offset >> 8]);
|
||||
const writeBuffer = Buffer.from([config, chunkSizeToRead, offset & 0xff, offset >> 8]);
|
||||
const readBuffer = await this.device.write(writeBuffer);
|
||||
configBuffer = Buffer.concat([configBuffer, new Buffer(readBuffer.slice(1, chunkSizeToRead + 1))]);
|
||||
offset += chunkSizeToRead;
|
||||
}
|
||||
response = UhkHidDeviceService.convertBufferToIntArray(configBuffer);
|
||||
response = UhkHidDevice.convertBufferToIntArray(configBuffer);
|
||||
return Promise.resolve(JSON.stringify(response));
|
||||
} catch (error) {
|
||||
this.logService.error('[DeviceService] getUserConfigFromEeprom error', error);
|
||||
} finally {
|
||||
this.device.close();
|
||||
const errMsg = `[DeviceService] ${configName} from eeprom error`;
|
||||
this.logService.error(errMsg, error);
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
|
||||
event.sender.send(IpcEvents.device.loadUserConfigurationReply, JSON.stringify(response));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,11 +127,11 @@ export class DeviceService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the UserConfiguration size from the UHK Device
|
||||
* Return the user / hardware configuration size from the UHK Device
|
||||
* @returns {Promise<number>}
|
||||
*/
|
||||
private async getUserConfigSizeFromKeyboard(): Promise<number> {
|
||||
const buffer = await this.device.write(new Buffer([Command.GetProperty, SystemPropertyIds.UserConfigSize]));
|
||||
private async getConfigSizeFromKeyboard(property: SystemPropertyIds): Promise<number> {
|
||||
const buffer = await this.device.write(new Buffer([UsbCommand.GetProperty, property]));
|
||||
const configSize = buffer[1] + (buffer[2] << 8);
|
||||
this.logService.debug('[DeviceService] User config size:', configSize);
|
||||
return configSize;
|
||||
@@ -144,7 +144,7 @@ export class DeviceService {
|
||||
this.logService.debug('[DeviceService] USB[T]: Write user configuration to keyboard');
|
||||
await this.sendUserConfigToKeyboard(json);
|
||||
this.logService.debug('[DeviceService] USB[T]: Write user configuration to EEPROM');
|
||||
await this.writeUserConfigToEeprom();
|
||||
await this.device.writeConfigToEeprom(EepromTransfer.WriteUserConfig);
|
||||
|
||||
response.success = true;
|
||||
}
|
||||
@@ -168,48 +168,12 @@ export class DeviceService {
|
||||
*/
|
||||
private async sendUserConfigToKeyboard(json: string): Promise<void> {
|
||||
const buffer: Buffer = new Buffer(JSON.parse(json).data);
|
||||
const fragments = this.getTransferBuffers(buffer);
|
||||
const fragments = UhkHidDevice.getTransferBuffers(UsbCommand.UploadUserConfig, buffer);
|
||||
for (const fragment of fragments) {
|
||||
await this.device.write(fragment);
|
||||
}
|
||||
this.logService.debug('[DeviceService] USB[T]: Apply user configuration to keyboard');
|
||||
const applyBuffer = new Buffer([Command.ApplyConfig]);
|
||||
const applyBuffer = new Buffer([UsbCommand.ApplyConfig]);
|
||||
await this.device.write(applyBuffer);
|
||||
}
|
||||
|
||||
private async writeUserConfigToEeprom(): Promise<void> {
|
||||
await this.device.write(new Buffer([Command.LaunchEepromTransfer, EepromTransfer.WriteUserConfig]));
|
||||
await this.waitUntilKeyboardBusy();
|
||||
}
|
||||
|
||||
private async waitUntilKeyboardBusy(): Promise<void> {
|
||||
while (true) {
|
||||
const buffer = await this.device.write(new Buffer([Command.GetKeyboardState]));
|
||||
if (buffer[1] === 0) {
|
||||
break;
|
||||
}
|
||||
this.logService.debug('Keyboard is busy, wait...');
|
||||
await snooze(200);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the whole UserConfiguration package into 64 byte fragments
|
||||
* @param {Buffer} configBuffer
|
||||
* @returns {Buffer[]}
|
||||
* @private
|
||||
*/
|
||||
private getTransferBuffers(configBuffer: Buffer): Buffer[] {
|
||||
const fragments: Buffer[] = [];
|
||||
const MAX_SENDING_PAYLOAD_SIZE = Constants.MAX_PAYLOAD_SIZE - 4;
|
||||
for (let offset = 0; offset < configBuffer.length; offset += MAX_SENDING_PAYLOAD_SIZE) {
|
||||
const length = offset + MAX_SENDING_PAYLOAD_SIZE < configBuffer.length
|
||||
? MAX_SENDING_PAYLOAD_SIZE
|
||||
: configBuffer.length - offset;
|
||||
const header = new Buffer([Command.UploadConfig, length, offset & 0xFF, offset >> 8]);
|
||||
fragments.push(Buffer.concat([header, configBuffer.slice(offset, offset + length)]));
|
||||
}
|
||||
|
||||
return fragments;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './src/util';
|
||||
export * from './src/models';
|
||||
export * from './src/services';
|
||||
export * from './src/config-serializer';
|
||||
|
||||
5
packages/uhk-common/package-lock.json
generated
5
packages/uhk-common/package-lock.json
generated
@@ -20,6 +20,11 @@
|
||||
"resolved": "https://registry.npmjs.org/@ngrx/store/-/store-2.2.3.tgz",
|
||||
"integrity": "sha1-570RSfHEQgjxzEdENT8PmKDx9Xs="
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "8.0.28",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.28.tgz",
|
||||
"integrity": "sha512-HupkFXEv3O3KSzcr3Ylfajg0kaerBg1DyaZzRBBQfrU3NN1mTBRE7sCveqHwXLS5Yrjvww8qFzkzYQQakG9FuQ=="
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.7.1.tgz",
|
||||
|
||||
@@ -3,15 +3,17 @@
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"description": "Common Library contains the common code for uhk-agent (electron-main) and web (electron-renderer) modules",
|
||||
"main": "src/index.js",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@angular/core": "^4.3.3",
|
||||
"@angular/core": "4.3.3",
|
||||
"@ngrx/core": "1.2.0",
|
||||
"@ngrx/store": "^2.2.3"
|
||||
"@ngrx/store": "2.2.3",
|
||||
"@types/node": "8.0.28"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ The configuration of the UHK is unusually complex for a keyboard, and is compose
|
||||
|
||||
## 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`.
|
||||
Given that the development dependencies are installed on your system you should be able to run the configuration serializer tester test by running `npm run test` in this directorx.
|
||||
|
||||
## 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 [user-config.json](user-config.json) for an example configuration.
|
||||
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 [user-config.json](../../../uhk-web/src/app/services/user-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.
|
||||
|
||||
@@ -75,7 +75,7 @@ KeyActions.toJsObject: <KeyActions length="9">
|
||||
|
||||
## 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.
|
||||
[test-serializer.ts](test-serializer.ts) is designed to test the serializer by taking [user-config.json](../../../uhk-web/src/app/services/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:
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
export * from './key-action';
|
||||
export * from './macro-action';
|
||||
export * from './hardware-configuration';
|
||||
export * from './key-modifiers';
|
||||
export * from './keymap';
|
||||
export * from './layer';
|
||||
export * from './long-press-action';
|
||||
export * from './macro';
|
||||
export * from './module';
|
||||
export * from './module-configuration';
|
||||
export * from './user-configuration';
|
||||
@@ -1,8 +1,9 @@
|
||||
export * from './key-action';
|
||||
export * from './keystroke-action';
|
||||
export * from './keystroke-type';
|
||||
export * from './mouse-action';
|
||||
export * from './none-action';
|
||||
export * from './play-macro-action';
|
||||
export * from './switch-keymap-action';
|
||||
export * from './switch-layer-action';
|
||||
export * from './helper';
|
||||
export { Helper as KeyActionHelper } from './helper';
|
||||
@@ -3,7 +3,6 @@ import { Layer } from './layer';
|
||||
import { Macro } from './macro';
|
||||
import { SwitchLayerAction } from './key-action/switch-layer-action';
|
||||
import { KeyAction } from './key-action/key-action';
|
||||
import { ConfigSerializer } from '../config-serializer';
|
||||
import { UserConfiguration } from './user-configuration';
|
||||
|
||||
export class Keymap {
|
||||
@@ -2,7 +2,6 @@ import { UhkBuffer } from '../uhk-buffer';
|
||||
import { Macro } from './macro';
|
||||
import { Module } from './module';
|
||||
import { UserConfiguration } from './user-configuration';
|
||||
import { ConfigSerializer } from '../config-serializer';
|
||||
|
||||
export class Layer {
|
||||
|
||||
@@ -5,4 +5,4 @@ export * from './move-mouse-macro-action';
|
||||
export * from './mouse-button-macro-action';
|
||||
export * from './scroll-mouse-macro-action';
|
||||
export * from './text-macro-action';
|
||||
export * from './helper';
|
||||
export { Helper as MacroActionHelper } from './helper';
|
||||
@@ -1,6 +1,7 @@
|
||||
import { assertUInt8 } from '../assert';
|
||||
import { UhkBuffer } from '../uhk-buffer';
|
||||
import { Helper as MacroActionHelper, MacroAction } from './macro-action';
|
||||
import { MacroAction } from './macro-action';
|
||||
import { Helper as MacroActionHelper } from './macro-action/helper';
|
||||
|
||||
export class Macro {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { assertEnum, assertUInt8 } from '../assert';
|
||||
import { UhkBuffer } from '../uhk-buffer';
|
||||
import { Helper as KeyActionHelper, KeyAction, NoneAction, PlayMacroAction, SwitchKeymapAction } from './key-action';
|
||||
import { KeyActionHelper, KeyAction, NoneAction, PlayMacroAction, SwitchKeymapAction } from './key-action';
|
||||
import { Macro } from './macro';
|
||||
import { UserConfiguration } from './user-configuration';
|
||||
|
||||
4
packages/uhk-common/src/config-serializer/index.ts
Normal file
4
packages/uhk-common/src/config-serializer/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './config-items';
|
||||
export * from './assert';
|
||||
export * from './config-serializer';
|
||||
export * from './uhk-buffer';
|
||||
@@ -4,6 +4,24 @@ export class UhkBuffer {
|
||||
(<any>element).toBinary(buffer); // TODO: Remove any
|
||||
}
|
||||
|
||||
static fromArray(data: Array<number>): UhkBuffer {
|
||||
if (data.length < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const uhkBuffer = new UhkBuffer();
|
||||
let hasNonZeroValue = false;
|
||||
for (const num of data) {
|
||||
if (num > 0) {
|
||||
hasNonZeroValue = true;
|
||||
}
|
||||
uhkBuffer.writeUInt8(num);
|
||||
}
|
||||
uhkBuffer.offset = 0;
|
||||
|
||||
return uhkBuffer;
|
||||
}
|
||||
|
||||
private static eepromSize = 32 * 1024;
|
||||
private static maxCompactLength = 0xFFFF;
|
||||
private static longCompactLengthPrefix = 0xFF;
|
||||
6
packages/uhk-common/src/models/configuration-reply.ts
Normal file
6
packages/uhk-common/src/models/configuration-reply.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface ConfigurationReply {
|
||||
success: boolean;
|
||||
userConfiguration?: string;
|
||||
hardwareConfiguration?: string;
|
||||
error?: string;
|
||||
}
|
||||
@@ -2,3 +2,4 @@ export * from './command-line-args';
|
||||
export * from './notification';
|
||||
export * from './ipc-response';
|
||||
export * from './app-start-info';
|
||||
export * from './configuration-reply';
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable()
|
||||
export class LogService {
|
||||
error(...args: any[]): void {
|
||||
console.error(args);
|
||||
}
|
||||
|
||||
debug(...args: any[]): void {
|
||||
console.debug(args);
|
||||
console.log(args);
|
||||
}
|
||||
|
||||
silly(...args: any[]): void {
|
||||
console.debug(args);
|
||||
console.log(args);
|
||||
}
|
||||
|
||||
info(...args: any[]): void {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export { Constants } from './constants';
|
||||
export { IpcEvents } from './ipcEvents';
|
||||
export * from './log';
|
||||
|
||||
// Source: http://stackoverflow.com/questions/13720256/javascript-regex-camelcase-to-sentence
|
||||
export function camelCaseToSentence(camelCasedText: string): string {
|
||||
|
||||
@@ -22,8 +22,8 @@ class Device {
|
||||
public static readonly deviceConnectionStateChanged = 'device-connection-state-changed';
|
||||
public static readonly saveUserConfiguration = 'device-save-user-configuration';
|
||||
public static readonly saveUserConfigurationReply = 'device-save-user-configuration-reply';
|
||||
public static readonly loadUserConfiguration = 'device-load-user-configuration';
|
||||
public static readonly loadUserConfigurationReply = 'device-load-user-configuration-reply';
|
||||
public static readonly loadConfigurations = 'device-load-configuration';
|
||||
public static readonly loadConfigurationReply = 'device-load-configuration-reply';
|
||||
}
|
||||
|
||||
export class IpcEvents {
|
||||
|
||||
6
packages/uhk-common/src/util/log.ts
Normal file
6
packages/uhk-common/src/util/log.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export namespace LogRegExps {
|
||||
export const transferRegExp = /USB\[T]:/;
|
||||
export const writeRegExp = /USB\[W]:/;
|
||||
export const readRegExp = /USB\[R]: 00/;
|
||||
export const errorRegExp = /(?:(USB\[R]: ([^0]|0[^0])))/;
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"declaration": false,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
|
||||
1
packages/uhk-usb/index.ts
Normal file
1
packages/uhk-usb/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './src';
|
||||
781
packages/uhk-usb/package-lock.json
generated
Normal file
781
packages/uhk-usb/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
17
packages/uhk-usb/package.json
Normal file
17
packages/uhk-usb/package.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "uhk-usb",
|
||||
"version": "1.0.0",
|
||||
"description": "Agent preliminary USB code",
|
||||
"main": "dist/index.js",
|
||||
"license": "GPL-3.0",
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "8.0.28"
|
||||
},
|
||||
"dependencies": {
|
||||
"node-hid": "0.5.4",
|
||||
"uhk-common": "1.0.0"
|
||||
}
|
||||
}
|
||||
35
packages/uhk-usb/src/constants.ts
Normal file
35
packages/uhk-usb/src/constants.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
export namespace Constants {
|
||||
export const VENDOR_ID = 0x1D50;
|
||||
export const PRODUCT_ID = 0x6122;
|
||||
export const MAX_PAYLOAD_SIZE = 64;
|
||||
}
|
||||
|
||||
/**
|
||||
* UHK USB Communications command. All communication package should have start with a command code.
|
||||
*/
|
||||
export enum UsbCommand {
|
||||
GetProperty = 0,
|
||||
UploadUserConfig = 8,
|
||||
ApplyConfig = 9,
|
||||
LaunchEepromTransfer = 12,
|
||||
ReadHardwareConfig = 13,
|
||||
WriteHardwareConfig = 14,
|
||||
ReadUserConfig = 15,
|
||||
GetKeyboardState = 16
|
||||
}
|
||||
|
||||
export enum EepromTransfer {
|
||||
ReadHardwareConfig = 0,
|
||||
WriteHardwareConfig = 1,
|
||||
ReadUserConfig = 2,
|
||||
WriteUserConfig = 3
|
||||
}
|
||||
|
||||
export enum SystemPropertyIds {
|
||||
UsbProtocolVersion = 0,
|
||||
BridgeProtocolVersion = 1,
|
||||
DataModelVersion = 2,
|
||||
FirmwareVersion = 3,
|
||||
HardwareConfigSize = 4,
|
||||
UserConfigSize = 5
|
||||
}
|
||||
2
packages/uhk-usb/src/index.ts
Normal file
2
packages/uhk-usb/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './constants';
|
||||
export * from './uhk-hid-device';
|
||||
@@ -1,11 +1,14 @@
|
||||
import { Device, devices, HID } from 'node-hid';
|
||||
import { LogService } from 'uhk-common';
|
||||
|
||||
import { Constants, LogService } from 'uhk-common';
|
||||
import { Constants, EepromTransfer, UsbCommand } from './constants';
|
||||
|
||||
const snooze = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
/**
|
||||
* HID API wrapper to support unified logging and async write
|
||||
*/
|
||||
export class UhkHidDeviceService {
|
||||
export class UhkHidDevice {
|
||||
/**
|
||||
* Convert the Buffer to number[]
|
||||
* @param {Buffer} buffer
|
||||
@@ -17,6 +20,27 @@ export class UhkHidDeviceService {
|
||||
return Array.prototype.slice.call(buffer, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the communication package into 64 byte fragments
|
||||
* @param {UsbCommand} usbCommand
|
||||
* @param {Buffer} configBuffer
|
||||
* @returns {Buffer[]}
|
||||
* @private
|
||||
*/
|
||||
public static getTransferBuffers(usbCommand: UsbCommand, configBuffer: Buffer): Buffer[] {
|
||||
const fragments: Buffer[] = [];
|
||||
const MAX_SENDING_PAYLOAD_SIZE = Constants.MAX_PAYLOAD_SIZE - 4;
|
||||
for (let offset = 0; offset < configBuffer.length; offset += MAX_SENDING_PAYLOAD_SIZE) {
|
||||
const length = offset + MAX_SENDING_PAYLOAD_SIZE < configBuffer.length
|
||||
? MAX_SENDING_PAYLOAD_SIZE
|
||||
: configBuffer.length - offset;
|
||||
const header = new Buffer([usbCommand, length, offset & 0xFF, offset >> 8]);
|
||||
fragments.push(Buffer.concat([header, configBuffer.slice(offset, offset + length)]));
|
||||
}
|
||||
|
||||
return fragments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the communication package that will send over USB and
|
||||
* - add usb report code as 1st byte
|
||||
@@ -27,7 +51,7 @@ export class UhkHidDeviceService {
|
||||
* @static
|
||||
*/
|
||||
private static getTransferData(buffer: Buffer): number[] {
|
||||
const data = UhkHidDeviceService.convertBufferToIntArray(buffer);
|
||||
const data = UhkHidDevice.convertBufferToIntArray(buffer);
|
||||
// if data start with 0 need to add additional leading zero because HID API remove it.
|
||||
// https://github.com/node-hid/node-hid/issues/187
|
||||
if (data.length > 0 && data[0] === 0 && process.platform === 'win32') {
|
||||
@@ -108,7 +132,7 @@ export class UhkHidDeviceService {
|
||||
this.logService.error('[UhkHidDevice] Transfer error: ', err);
|
||||
return reject(err);
|
||||
}
|
||||
const logString = UhkHidDeviceService.bufferToString(receivedData);
|
||||
const logString = UhkHidDevice.bufferToString(receivedData);
|
||||
this.logService.debug('[UhkHidDevice] USB[R]:', logString);
|
||||
|
||||
if (receivedData[0] !== 0) {
|
||||
@@ -118,12 +142,17 @@ export class UhkHidDeviceService {
|
||||
return resolve(Buffer.from(receivedData));
|
||||
});
|
||||
|
||||
const sendData = UhkHidDeviceService.getTransferData(buffer);
|
||||
this.logService.debug('[UhkHidDevice] USB[W]:', UhkHidDeviceService.bufferToString(sendData));
|
||||
const sendData = UhkHidDevice.getTransferData(buffer);
|
||||
this.logService.debug('[UhkHidDevice] USB[W]:', UhkHidDevice.bufferToString(sendData));
|
||||
device.write(sendData);
|
||||
});
|
||||
}
|
||||
|
||||
public async writeConfigToEeprom(transferType: EepromTransfer): Promise<void> {
|
||||
await this.write(new Buffer([UsbCommand.LaunchEepromTransfer, transferType]));
|
||||
await this.waitUntilKeyboardBusy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the communication chanel with UHK Device
|
||||
*/
|
||||
@@ -136,6 +165,17 @@ export class UhkHidDeviceService {
|
||||
this._device = null;
|
||||
}
|
||||
|
||||
private async waitUntilKeyboardBusy(): Promise<void> {
|
||||
while (true) {
|
||||
const buffer = await this.write(new Buffer([UsbCommand.GetKeyboardState]));
|
||||
if (buffer[1] === 0) {
|
||||
break;
|
||||
}
|
||||
this.logService.debug('Keyboard is busy, wait...');
|
||||
await snooze(200);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the stored version of HID device. If not exist try to initialize.
|
||||
* @returns {HID}
|
||||
21
packages/uhk-usb/tsconfig.json
Normal file
21
packages/uhk-usb/tsconfig.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"declaration": false,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "es2016",
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
],
|
||||
"lib": [
|
||||
"es2015.iterable",
|
||||
"dom",
|
||||
"es2016"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { Component, HostListener, ViewEncapsulation } from '@angular/core';
|
||||
import { animate, style, transition, trigger } from '@angular/animations';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Action, Store } from '@ngrx/store';
|
||||
import { UhkBuffer } from 'uhk-common';
|
||||
|
||||
import 'rxjs/add/operator/last';
|
||||
|
||||
@@ -14,7 +15,6 @@ import {
|
||||
saveToKeyboardState
|
||||
} from './store';
|
||||
import { getUserConfiguration } from './store/reducers/user-configuration';
|
||||
import { UhkBuffer } from './config-serializer/uhk-buffer';
|
||||
import { ProgressButtonState } from './store/reducers/progress-button-state';
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<svg-keyboard *ngFor="let layer of layers; let index = index; trackBy: trackKeyboard"
|
||||
[@layerState]="layerAnimationState[index]"
|
||||
[moduleConfig]="layer.modules"
|
||||
[keybindAnimationEnabled]="keybindAnimationEnabled"
|
||||
[halvesSplit]="halvesSplit"
|
||||
[capturingEnabled]="capturingEnabled"
|
||||
[selectedKey]="selectedKey"
|
||||
[selected]="selectedKey?.layerId === index"
|
||||
(keyClick)="keyClick.emit($event)"
|
||||
(keyHover)="keyHover.emit($event)"
|
||||
(capture)="capture.emit($event)"
|
||||
[@layerState]="layerAnimationState[index]"
|
||||
[moduleConfig]="layer.modules"
|
||||
[keybindAnimationEnabled]="keybindAnimationEnabled"
|
||||
[halvesSplit]="halvesSplit"
|
||||
[capturingEnabled]="capturingEnabled"
|
||||
[selectedKey]="selectedKey"
|
||||
[selected]="selectedKey?.layerId === index"
|
||||
[keyboardLayout]="keyboardLayout"
|
||||
(keyClick)="keyClick.emit($event)"
|
||||
(keyHover)="keyHover.emit($event)"
|
||||
(capture)="capture.emit($event)"
|
||||
>
|
||||
</svg-keyboard>
|
||||
|
||||
|
Before Width: | Height: | Size: 511 B After Width: | Height: | Size: 659 B |
@@ -1,7 +1,8 @@
|
||||
import { Component, ChangeDetectionStrategy, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { animate, keyframes, state, style, transition, trigger } from '@angular/animations';
|
||||
import { Layer } from 'uhk-common';
|
||||
|
||||
import { Layer } from '../../../config-serializer/config-items/layer';
|
||||
import { KeyboardLayout } from '../../../keyboard/keyboard-layout.enum';
|
||||
|
||||
type AnimationKeyboard =
|
||||
'leftIn' |
|
||||
@@ -69,6 +70,7 @@ export class KeyboardSliderComponent implements OnChanges {
|
||||
@Input() capturingEnabled: boolean;
|
||||
@Input() halvesSplit: boolean;
|
||||
@Input() selectedKey: { layerId: number, moduleId: number, keyId: number };
|
||||
@Input() keyboardLayout = KeyboardLayout.ANSI;
|
||||
@Output() keyClick = new EventEmitter();
|
||||
@Output() keyHover = new EventEmitter();
|
||||
@Output() capture = new EventEmitter();
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Keymap } from 'uhk-common';
|
||||
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/combineLatest';
|
||||
import 'rxjs/add/operator/publishReplay';
|
||||
|
||||
import { Keymap } from '../../../config-serializer/config-items/keymap';
|
||||
import { AppState } from '../../../store';
|
||||
import { KeymapActions } from '../../../store/actions';
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CanActivate, Router } from '@angular/router';
|
||||
import { Keymap } from 'uhk-common';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
@@ -10,7 +11,6 @@ import 'rxjs/add/operator/switchMap';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { Keymap } from '../../../config-serializer/config-items/keymap';
|
||||
import { AppState } from '../../../store/index';
|
||||
import { getKeymaps } from '../../../store/reducers/user-configuration';
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<ng-template [ngIf]="keymap$ | async">
|
||||
<keymap-header [keymap]="keymap$ | async" [deletable]="deletable$ | async" (downloadClick)="downloadKeymap()"></keymap-header>
|
||||
<svg-keyboard-wrap [keymap]="keymap$ | async" [halvesSplit]="keyboardSplit"></svg-keyboard-wrap>
|
||||
<keymap-header [keymap]="keymap$ | async"
|
||||
[deletable]="deletable$ | async"
|
||||
(downloadClick)="downloadKeymap()"></keymap-header>
|
||||
<svg-keyboard-wrap [keymap]="keymap$ | async"
|
||||
[halvesSplit]="keyboardSplit"
|
||||
[keyboardLayout]="keyboardLayout$ | async"></svg-keyboard-wrap>
|
||||
</ng-template>
|
||||
|
||||
<div *ngIf="!(keymap$ | async)" class="not-found">
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Component, HostListener, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Keymap } from 'uhk-common';
|
||||
|
||||
import '@ngrx/core/add/operator/select';
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/first';
|
||||
@@ -15,11 +16,11 @@ import 'rxjs/add/operator/combineLatest';
|
||||
|
||||
import { saveAs } from 'file-saver';
|
||||
|
||||
import { Keymap } from '../../../config-serializer/config-items/keymap';
|
||||
import { AppState } from '../../../store';
|
||||
import { AppState, getKeyboardLayout } from '../../../store';
|
||||
import { getKeymap, getKeymaps, getUserConfiguration } from '../../../store/reducers/user-configuration';
|
||||
import 'rxjs/add/operator/pluck';
|
||||
import { SvgKeyboardWrapComponent } from '../../svg/wrap/svg-keyboard-wrap.component';
|
||||
import { KeyboardLayout } from '../../../keyboard/keyboard-layout.enum';
|
||||
|
||||
@Component({
|
||||
selector: 'keymap-edit',
|
||||
@@ -37,6 +38,7 @@ export class KeymapEditComponent {
|
||||
|
||||
deletable$: Observable<boolean>;
|
||||
keymap$: Observable<Keymap>;
|
||||
keyboardLayout$: Observable<KeyboardLayout>;
|
||||
|
||||
constructor(protected store: Store<AppState>,
|
||||
route: ActivatedRoute) {
|
||||
@@ -49,6 +51,8 @@ export class KeymapEditComponent {
|
||||
|
||||
this.deletable$ = store.let(getKeymaps())
|
||||
.map((keymaps: Keymap[]) => keymaps.length > 1);
|
||||
|
||||
this.keyboardLayout$ = store.select(getKeyboardLayout);
|
||||
}
|
||||
|
||||
downloadKeymap() {
|
||||
|
||||
@@ -10,11 +10,10 @@ import {
|
||||
SimpleChanges,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { Keymap } from 'uhk-common';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { Keymap } from '../../../config-serializer/config-items/keymap';
|
||||
|
||||
import { AppState } from '../../../store';
|
||||
import { KeymapActions } from '../../../store/actions';
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ import {
|
||||
MoveMouseMacroAction,
|
||||
MouseButtonMacroAction,
|
||||
TextMacroAction,
|
||||
Helper as MacroActionHelper
|
||||
} from '../../../config-serializer/config-items/macro-action';
|
||||
MacroActionHelper
|
||||
} from 'uhk-common';
|
||||
import { MacroDelayTabComponent, MacroMouseTabComponent, MacroKeyTabComponent, MacroTextTabComponent } from './tab';
|
||||
|
||||
enum TabName {
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
OnInit,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { DelayMacroAction } from 'uhk-common';
|
||||
|
||||
import { DelayMacroAction } from '../../../../../config-serializer/config-items/macro-action';
|
||||
import { MacroBaseComponent } from '../macro-base.component';
|
||||
|
||||
const INITIAL_DELAY = 0.5; // In seconds
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Component, Input, OnInit, ViewChild } from '@angular/core';
|
||||
|
||||
import { KeystrokeAction } from '../../../../../config-serializer/config-items/key-action';
|
||||
import { KeyMacroAction, MacroSubAction } from '../../../../../config-serializer/config-items/macro-action';
|
||||
import { KeyMacroAction, KeystrokeAction, MacroSubAction } from 'uhk-common';
|
||||
import { KeypressTabComponent, Tab } from '../../../../popover/tab';
|
||||
import { MacroBaseComponent } from '../macro-base.component';
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
MoveMouseMacroAction,
|
||||
ScrollMouseMacroAction,
|
||||
MacroSubAction
|
||||
} from '../../../../../config-serializer/config-items/macro-action';
|
||||
} from 'uhk-common';
|
||||
import { Tab } from '../../../../popover/tab';
|
||||
import { MacroBaseComponent } from '../macro-base.component';
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
Renderer,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { TextMacroAction } from 'uhk-common';
|
||||
|
||||
import { TextMacroAction } from '../../../../../config-serializer/config-items/macro-action';
|
||||
import { MacroBaseComponent } from '../macro-base.component';
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Macro, MacroAction } from 'uhk-common';
|
||||
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import 'rxjs/add/operator/pluck';
|
||||
|
||||
import { Macro } from '../../../config-serializer/config-items/macro';
|
||||
import { MacroAction } from '../../../config-serializer/config-items/macro-action/macro-action';
|
||||
|
||||
import { MacroActions } from '../../../store/actions';
|
||||
import { AppState } from '../../../store/index';
|
||||
import { getMacro } from '../../../store/reducers/user-configuration';
|
||||
|
||||
@@ -9,10 +9,8 @@ import {
|
||||
SimpleChanges,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { Macro } from '../../../config-serializer/config-items/macro';
|
||||
import { Macro } from 'uhk-common';
|
||||
|
||||
import { MacroActions } from '../../../store/actions';
|
||||
import { AppState } from '../../../store/index';
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { Component, Input, Output, EventEmitter, OnChanges, OnInit, SimpleChanges } from '@angular/core';
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
|
||||
import { KeyModifiers } from '../../../config-serializer/config-items/key-modifiers';
|
||||
import {
|
||||
DelayMacroAction,
|
||||
KeyMacroAction,
|
||||
KeyModifiers,
|
||||
MacroAction,
|
||||
MouseButtonMacroAction,
|
||||
MoveMouseMacroAction,
|
||||
ScrollMouseMacroAction,
|
||||
TextMacroAction
|
||||
} from '../../../config-serializer/config-items/macro-action';
|
||||
} from 'uhk-common';
|
||||
|
||||
import { MapperService } from '../../../services/mapper.service';
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { Component, EventEmitter, Input, Output, QueryList, ViewChildren, forwardRef } from '@angular/core';
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
|
||||
import { DragulaService } from 'ng2-dragula/ng2-dragula';
|
||||
import { Macro, MacroAction } from 'uhk-common';
|
||||
|
||||
import { Macro } from '../../../config-serializer/config-items/macro';
|
||||
import { MacroAction } from '../../../config-serializer/config-items/macro-action';
|
||||
import { MacroItemComponent } from '../item';
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CanActivate, Router } from '@angular/router';
|
||||
import { Macro } from 'uhk-common';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
@@ -10,7 +11,6 @@ import { Store } from '@ngrx/store';
|
||||
|
||||
import { AppState } from '../../../store/index';
|
||||
import { getMacros } from '../../../store/reducers/user-configuration';
|
||||
import { Macro } from '../../../config-serializer/config-items/macro';
|
||||
|
||||
@Injectable()
|
||||
export class MacroNotFoundGuard implements CanActivate {
|
||||
|
||||
@@ -19,13 +19,13 @@ import 'rxjs/add/operator/map';
|
||||
|
||||
import {
|
||||
KeyAction,
|
||||
Keymap,
|
||||
KeystrokeAction,
|
||||
MouseAction,
|
||||
PlayMacroAction,
|
||||
SwitchKeymapAction,
|
||||
SwitchLayerAction
|
||||
} from '../../config-serializer/config-items/key-action';
|
||||
import { Keymap } from '../../config-serializer/config-items/keymap';
|
||||
} from 'uhk-common';
|
||||
|
||||
import { Tab } from './tab/tab';
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
|
||||
|
||||
import { Select2OptionData } from 'ng2-select2/ng2-select2';
|
||||
import { Keymap, KeyAction, SwitchKeymapAction } from 'uhk-common';
|
||||
|
||||
import { KeyAction, SwitchKeymapAction } from '../../../../config-serializer/config-items/key-action';
|
||||
import { Keymap } from '../../../../config-serializer/config-items/keymap';
|
||||
import { Tab } from '../tab';
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import { Component, Input, OnChanges } from '@angular/core';
|
||||
|
||||
import { Select2OptionData, Select2TemplateFunction } from 'ng2-select2';
|
||||
|
||||
import { KeyAction, KeystrokeAction } from '../../../../config-serializer/config-items/key-action';
|
||||
import { KeyAction, KeystrokeAction, KeystrokeType } from 'uhk-common';
|
||||
|
||||
import { Tab } from '../tab';
|
||||
import { MapperService } from '../../../../services/mapper.service';
|
||||
import { KeystrokeType } from '../../../../config-serializer/config-items/key-action/keystroke-type';
|
||||
|
||||
@Component({
|
||||
selector: 'keypress-tab',
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Component, HostBinding, Input, OnChanges, SimpleChanges } from '@angular/core';
|
||||
|
||||
import { KeyAction, LayerName, SwitchLayerAction } from '../../../../config-serializer/config-items/key-action';
|
||||
import { KeyAction, LayerName, SwitchLayerAction } from 'uhk-common';
|
||||
|
||||
import { Tab } from '../tab';
|
||||
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
|
||||
import { Select2OptionData } from 'ng2-select2/ng2-select2';
|
||||
|
||||
import { KeyAction, PlayMacroAction } from '../../../../config-serializer/config-items/key-action';
|
||||
import { Macro } from '../../../../config-serializer/config-items/macro';
|
||||
import { KeyAction, Macro, PlayMacroAction } from 'uhk-common';
|
||||
|
||||
import { Tab } from '../tab';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, Input, OnChanges } from '@angular/core';
|
||||
import { KeyAction, MouseAction, MouseActionParam } from 'uhk-common';
|
||||
|
||||
import { KeyAction, MouseAction, MouseActionParam } from '../../../../config-serializer/config-items/key-action';
|
||||
import { Tab } from '../tab';
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { EventEmitter, Output } from '@angular/core';
|
||||
|
||||
import { KeyAction } from '../../../config-serializer/config-items/key-action';
|
||||
import { KeyAction } from 'uhk-common';
|
||||
|
||||
export abstract class Tab {
|
||||
@Output() validAction = new EventEmitter<boolean>();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Component, Renderer } from '@angular/core';
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
import { Keymap, Macro } from 'uhk-common';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
@@ -8,9 +9,6 @@ import 'rxjs/add/operator/do';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/let';
|
||||
|
||||
import { Keymap } from '../../config-serializer/config-items/keymap';
|
||||
import { Macro } from '../../config-serializer/config-items/macro';
|
||||
|
||||
import { AppState, showAddonMenu, runningInElectron } from '../../store';
|
||||
import { MacroActions } from '../../store/actions';
|
||||
import { getKeymaps, getMacros } from '../../store/reducers/user-configuration';
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output, SimpleChanges, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { animate, state, trigger, style, transition } from '@angular/animations';
|
||||
import { Module } from 'uhk-common';
|
||||
|
||||
import { Module } from '../../../config-serializer/config-items/module';
|
||||
import { SvgModule } from '../module';
|
||||
import { SvgModuleProviderService } from '../../../services/svg-module-provider.service';
|
||||
import { KeyboardLayout } from '../../../keyboard/keyboard-layout.enum';
|
||||
|
||||
@Component({
|
||||
selector: 'svg-keyboard',
|
||||
@@ -29,6 +30,7 @@ export class SvgKeyboardComponent implements OnInit {
|
||||
@Input() selectedKey: { layerId: number, moduleId: number, keyId: number };
|
||||
@Input() selected: boolean;
|
||||
@Input() halvesSplit: boolean;
|
||||
@Input() keyboardLayout = KeyboardLayout.ANSI;
|
||||
@Output() keyClick = new EventEmitter();
|
||||
@Output() keyHover = new EventEmitter();
|
||||
@Output() capture = new EventEmitter();
|
||||
@@ -45,13 +47,17 @@ export class SvgKeyboardComponent implements OnInit {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.modules = this.svgModuleProvider.getSvgModules();
|
||||
this.setModules();
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes.halvesSplit) {
|
||||
this.updateModuleAnimationStates();
|
||||
}
|
||||
|
||||
if (changes['keyboardLayout']) {
|
||||
this.setModules();
|
||||
}
|
||||
}
|
||||
|
||||
onKeyClick(moduleId: number, keyId: number, keyTarget: HTMLElement): void {
|
||||
@@ -87,4 +93,7 @@ export class SvgKeyboardComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
private setModules() {
|
||||
this.modules = this.svgModuleProvider.getSvgModules(this.keyboardLayout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,15 +10,15 @@ import { Subscription } from 'rxjs/Subscription';
|
||||
|
||||
import {
|
||||
KeyAction,
|
||||
KeyModifiers,
|
||||
KeystrokeAction,
|
||||
LayerName,
|
||||
Macro,
|
||||
MouseAction,
|
||||
PlayMacroAction,
|
||||
SwitchKeymapAction,
|
||||
SwitchLayerAction
|
||||
} from '../../../../config-serializer/config-items/key-action';
|
||||
import { KeyModifiers } from '../../../../config-serializer/config-items/key-modifiers';
|
||||
import { Macro } from '../../../../config-serializer/config-items/macro';
|
||||
} from 'uhk-common';
|
||||
|
||||
import { CaptureService } from '../../../../services/capture.service';
|
||||
import { MapperService } from '../../../../services/mapper.service';
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Component, Input, OnChanges, OnInit, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { KeyModifiers, KeystrokeAction } from 'uhk-common';
|
||||
|
||||
import { KeystrokeAction } from '../../../../config-serializer/config-items/key-action';
|
||||
import { KeyModifiers } from '../../../../config-serializer/config-items/key-modifiers';
|
||||
import { MapperService } from '../../../../services/mapper.service';
|
||||
|
||||
class SvgAttributes {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, Input, OnChanges, ChangeDetectionStrategy } from '@angular/core';
|
||||
|
||||
import { MouseAction, MouseActionParam } from '../../../../config-serializer/config-items/key-action';
|
||||
import { MouseAction, MouseActionParam } from 'uhk-common';
|
||||
|
||||
@Component({
|
||||
selector: 'g[svg-mouse-key]',
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Component, EventEmitter, Input, Output, ChangeDetectionStrategy } from '@angular/core';
|
||||
|
||||
import { KeyAction } from '../../../config-serializer/config-items/key-action';
|
||||
import { KeyAction } from 'uhk-common';
|
||||
|
||||
import { SvgKeyboardKey } from '../keys';
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user