Seperate electron and web target building
This commit is contained in:
committed by
József Farkas
parent
517aed1b1c
commit
983eb72892
1
electron/src/.gitignore
vendored
Normal file
1
electron/src/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
shared
|
||||
166
electron/src/app.module.ts
Normal file
166
electron/src/app.module.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
import { NgModule, ReflectiveInjector } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { EffectsModule } from '@ngrx/effects';
|
||||
import { StoreModule } from '@ngrx/store';
|
||||
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
|
||||
import { StoreLogMonitorModule, useLogMonitor } from '@ngrx/store-log-monitor';
|
||||
|
||||
import { DragulaModule } from 'ng2-dragula/ng2-dragula';
|
||||
import { Select2Module } from 'ng2-select2/ng2-select2';
|
||||
|
||||
import { AddOnComponent } from './shared/components/add-on';
|
||||
import { KeyboardSliderComponent } from './shared/components/keyboard/slider';
|
||||
import { KeymapAddComponent, KeymapHeaderComponent } from './shared/components/keymap';
|
||||
import { KeymapEditComponent } from './components/keymap/edit';
|
||||
import { LayersComponent } from './shared/components/layers';
|
||||
import {
|
||||
MacroActionEditorComponent,
|
||||
MacroDelayTabComponent,
|
||||
MacroEditComponent,
|
||||
MacroHeaderComponent,
|
||||
MacroItemComponent,
|
||||
MacroKeyTabComponent,
|
||||
MacroListComponent,
|
||||
MacroMouseTabComponent,
|
||||
MacroNotFoundComponent,
|
||||
MacroTextTabComponent
|
||||
} from './shared/components/macro';
|
||||
import { NotificationComponent } from './shared/components/notification';
|
||||
import { PopoverComponent } from './shared/components/popover';
|
||||
import {
|
||||
KeymapTabComponent,
|
||||
KeypressTabComponent,
|
||||
LayerTabComponent,
|
||||
MacroTabComponent,
|
||||
MouseTabComponent,
|
||||
NoneTabComponent
|
||||
} from './shared/components/popover/tab';
|
||||
import { CaptureKeystrokeButtonComponent } from './shared/components/popover/widgets/capture-keystroke';
|
||||
import { IconComponent } from './shared/components/popover/widgets/icon';
|
||||
import { SettingsComponent } from './shared/components/settings';
|
||||
import { SideMenuComponent } from './shared/components/side-menu';
|
||||
import { SvgKeyboardComponent } from './shared/components/svg/keyboard';
|
||||
import {
|
||||
SvgIconTextKeyComponent,
|
||||
SvgKeyboardKeyComponent,
|
||||
SvgKeystrokeKeyComponent,
|
||||
SvgMouseClickKeyComponent,
|
||||
SvgMouseKeyComponent,
|
||||
SvgMouseMoveKeyComponent,
|
||||
SvgMouseScrollKeyComponent,
|
||||
SvgMouseSpeedKeyComponent,
|
||||
SvgOneLineTextKeyComponent,
|
||||
SvgSingleIconKeyComponent,
|
||||
SvgSwitchKeymapKeyComponent,
|
||||
SvgTextIconKeyComponent,
|
||||
SvgTwoLineTextKeyComponent
|
||||
} from './shared/components/svg/keys';
|
||||
import { SvgModuleComponent } from './shared/components/svg/module';
|
||||
import { SvgKeyboardWrapComponent } from './shared/components/svg/wrap';
|
||||
import { MainAppComponent, appRoutingProviders, routing } from './main-app';
|
||||
|
||||
import { CancelableDirective } from './shared/directives';
|
||||
|
||||
import { CaptureService } from './shared/services/capture.service';
|
||||
import { MapperService } from './shared/services/mapper.service';
|
||||
import { UhkDeviceService } from './services/uhk-device.service';
|
||||
|
||||
import { KeymapEffects, MacroEffects } from './shared/store/effects';
|
||||
import { keymapReducer, macroReducer, presetReducer } from './shared/store/reducers';
|
||||
import { DataStorage } from './shared/store/storage';
|
||||
|
||||
import { KeymapEditGuard } from './shared/components/keymap/edit';
|
||||
import { MacroNotFoundGuard } from './shared/components/macro/not-found';
|
||||
|
||||
// Create DataStorage dependency injection
|
||||
const storageProvider = ReflectiveInjector.resolve([DataStorage]);
|
||||
const storageInjector = ReflectiveInjector.fromResolvedProviders(storageProvider);
|
||||
const storageService: DataStorage = storageInjector.get(DataStorage);
|
||||
|
||||
// All reducers that are used in application
|
||||
const storeConfig = {
|
||||
keymaps: storageService.saveState(keymapReducer),
|
||||
macros: storageService.saveState(macroReducer),
|
||||
presetKeymaps: presetReducer
|
||||
};
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
MainAppComponent,
|
||||
KeymapEditComponent,
|
||||
KeymapHeaderComponent,
|
||||
NotificationComponent,
|
||||
SvgIconTextKeyComponent,
|
||||
SvgKeyboardKeyComponent,
|
||||
SvgKeystrokeKeyComponent,
|
||||
SvgMouseKeyComponent,
|
||||
SvgMouseClickKeyComponent,
|
||||
SvgMouseMoveKeyComponent,
|
||||
SvgMouseScrollKeyComponent,
|
||||
SvgMouseSpeedKeyComponent,
|
||||
SvgOneLineTextKeyComponent,
|
||||
SvgSingleIconKeyComponent,
|
||||
SvgSwitchKeymapKeyComponent,
|
||||
SvgTextIconKeyComponent,
|
||||
SvgTwoLineTextKeyComponent,
|
||||
SvgKeyboardKeyComponent,
|
||||
SvgKeyboardWrapComponent,
|
||||
SvgKeyboardComponent,
|
||||
SvgModuleComponent,
|
||||
LayersComponent,
|
||||
PopoverComponent,
|
||||
KeymapAddComponent,
|
||||
SideMenuComponent,
|
||||
KeypressTabComponent,
|
||||
KeymapTabComponent,
|
||||
LayerTabComponent,
|
||||
MacroTabComponent,
|
||||
MouseTabComponent,
|
||||
NoneTabComponent,
|
||||
CaptureKeystrokeButtonComponent,
|
||||
IconComponent,
|
||||
MacroEditComponent,
|
||||
MacroListComponent,
|
||||
MacroHeaderComponent,
|
||||
MacroItemComponent,
|
||||
MacroActionEditorComponent,
|
||||
MacroDelayTabComponent,
|
||||
MacroKeyTabComponent,
|
||||
MacroMouseTabComponent,
|
||||
MacroTextTabComponent,
|
||||
MacroNotFoundComponent,
|
||||
AddOnComponent,
|
||||
SettingsComponent,
|
||||
KeyboardSliderComponent,
|
||||
CancelableDirective
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
DragulaModule,
|
||||
routing,
|
||||
StoreModule.provideStore(storeConfig, storageService.initialState()),
|
||||
StoreDevtoolsModule.instrumentStore({
|
||||
monitor: useLogMonitor({
|
||||
visible: false,
|
||||
position: 'right'
|
||||
})
|
||||
}),
|
||||
StoreLogMonitorModule,
|
||||
Select2Module,
|
||||
EffectsModule.runAfterBootstrap(KeymapEffects),
|
||||
EffectsModule.runAfterBootstrap(MacroEffects)
|
||||
],
|
||||
providers: [
|
||||
MapperService,
|
||||
appRoutingProviders,
|
||||
KeymapEditGuard,
|
||||
MacroNotFoundGuard,
|
||||
CaptureService,
|
||||
UhkDeviceService
|
||||
],
|
||||
bootstrap: [MainAppComponent]
|
||||
})
|
||||
export class AppModule { }
|
||||
1
electron/src/components/keymap/edit/index.ts
Normal file
1
electron/src/components/keymap/edit/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { KeymapEditComponent } from './keymap-edit.component';
|
||||
93
electron/src/components/keymap/edit/keymap-edit.component.ts
Normal file
93
electron/src/components/keymap/edit/keymap-edit.component.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { Component, HostListener, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
import '@ngrx/core/add/operator/select';
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import 'rxjs/add/operator/do';
|
||||
import 'rxjs/add/operator/first';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/switchMap';
|
||||
|
||||
import { Keymap } from '../../../shared/config-serializer/config-items/Keymap';
|
||||
import { UhkBuffer } from '../../../shared/config-serializer/UhkBuffer';
|
||||
import { AppState } from '../../../shared/store';
|
||||
import { SvgKeyboardWrapComponent } from '../../../shared/components/svg/wrap';
|
||||
import { KeymapEditComponent as SharedKeymapEditComponent } from '../../../shared/components/keymap/edit';
|
||||
|
||||
import { UhkDeviceService } from '../../../services/uhk-device.service';
|
||||
|
||||
@Component({
|
||||
selector: 'keymap-edit',
|
||||
template: require('../../../shared/components/keymap/edit/keymap-edit.component.html'),
|
||||
styles: [require('../../../shared/components/keymap/edit/keymap-edit.component.scss')],
|
||||
host: {
|
||||
'class': 'container-fluid'
|
||||
}
|
||||
})
|
||||
export class KeymapEditComponent extends SharedKeymapEditComponent {
|
||||
|
||||
@ViewChild(SvgKeyboardWrapComponent) wrap: SvgKeyboardWrapComponent;
|
||||
|
||||
constructor(
|
||||
store: Store<AppState>,
|
||||
route: ActivatedRoute,
|
||||
private uhkDevice: UhkDeviceService
|
||||
) {
|
||||
super(store, route);
|
||||
}
|
||||
|
||||
@HostListener('window:keydown.control.u', ['$event'])
|
||||
onCtrlU(event: KeyboardEvent): void {
|
||||
console.log('ctrl + u pressed');
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.sendLayer();
|
||||
}
|
||||
|
||||
@HostListener('window:keydown.control.i', ['$event'])
|
||||
onCtrlI(event: KeyboardEvent): void {
|
||||
console.log('ctrl + i pressed');
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.sendKeymap();
|
||||
}
|
||||
|
||||
private sendLayer(): void {
|
||||
const currentLayer: number = this.wrap.getSelectedLayer();
|
||||
this.keymap$
|
||||
.first()
|
||||
.map(keymap => keymap.layers[currentLayer])
|
||||
.map(layer => {
|
||||
const uhkBuffer = new UhkBuffer();
|
||||
layer.toBinary(uhkBuffer);
|
||||
return uhkBuffer.getBufferContent();
|
||||
})
|
||||
.switchMap((buffer: Buffer) => this.uhkDevice.sendConfig(buffer))
|
||||
.do(response => console.log('Sending layer finished', response))
|
||||
.switchMap(() => this.uhkDevice.applyConfig())
|
||||
.subscribe(
|
||||
(response) => console.log('Applying layer finished', response),
|
||||
error => console.error('Error during uploading layer', error),
|
||||
() => console.log('Layer has been sucessfully uploaded')
|
||||
);
|
||||
}
|
||||
|
||||
private sendKeymap(): void {
|
||||
this.keymap$
|
||||
.first()
|
||||
.map(keymap => {
|
||||
const uhkBuffer = new UhkBuffer();
|
||||
keymap.toBinary(uhkBuffer);
|
||||
return uhkBuffer.getBufferContent();
|
||||
})
|
||||
.switchMap((buffer: Buffer) => this.uhkDevice.sendConfig(buffer))
|
||||
.do(response => console.log('Sending keymap finished', response))
|
||||
.switchMap(() => this.uhkDevice.applyConfig())
|
||||
.subscribe(
|
||||
(response) => console.log('Applying keymap finished', response),
|
||||
error => console.error('Error during uploading keymap', error),
|
||||
() => console.log('Keymap has been sucessfully uploaded')
|
||||
);
|
||||
}
|
||||
}
|
||||
1
electron/src/components/keymap/index.ts
Normal file
1
electron/src/components/keymap/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './keymap.routes';
|
||||
26
electron/src/components/keymap/keymap.routes.ts
Normal file
26
electron/src/components/keymap/keymap.routes.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
import { KeymapAddComponent } from '../../shared/components/keymap/add/keymap-add.component';
|
||||
import { KeymapEditComponent } from './edit';
|
||||
import { KeymapEditGuard } from '../../shared/components/keymap/edit';
|
||||
|
||||
export const keymapRoutes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: '/keymap',
|
||||
pathMatch: 'full'
|
||||
},
|
||||
{
|
||||
path: 'keymap',
|
||||
component: KeymapEditComponent,
|
||||
canActivate: [KeymapEditGuard]
|
||||
},
|
||||
{
|
||||
path: 'keymap/add',
|
||||
component: KeymapAddComponent
|
||||
},
|
||||
{
|
||||
path: 'keymap/:abbr',
|
||||
component: KeymapEditComponent
|
||||
}
|
||||
];
|
||||
55
electron/src/electron-main.ts
Normal file
55
electron/src/electron-main.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { BrowserWindow, app } from 'electron';
|
||||
import * as path from 'path';
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let win: Electron.BrowserWindow;
|
||||
|
||||
function createWindow() {
|
||||
// Create the browser window.
|
||||
win = new BrowserWindow({
|
||||
width: 1024,
|
||||
height: 768,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
});
|
||||
win.maximize();
|
||||
|
||||
const indexPath = path.resolve(__dirname, './index.html');
|
||||
// and load the index.html of the app.
|
||||
win.loadURL(`file://${indexPath}`);
|
||||
|
||||
// Emitted when the window is closed.
|
||||
win.on('closed', () => {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
win = null;
|
||||
});
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on('ready', createWindow);
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', () => {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
app.on('activate', () => {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (win === null) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here
|
||||
34
electron/src/index.html
Normal file
34
electron/src/index.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Ultimate Hacking Keyboard Configurator</title>
|
||||
<link href="vendor/font-awesome/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="shortcut icon" href="images/favicon.ico">
|
||||
<script src="polyfills.uhk.js"></script>
|
||||
<script src="vendor.uhk.js"></script>
|
||||
<!--<script src="vendor/usb/usb.js"></script>-->
|
||||
<script>
|
||||
usb = require('usb');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Google Tag Manager -->
|
||||
<noscript>
|
||||
<iframe src="//www.googletagmanager.com/ns.html?id=GTM-PQLCXB" height="0" width="0" style="display:none;visibility:hidden"></iframe>
|
||||
</noscript>
|
||||
<script>
|
||||
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||
'//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||
})(window,document,'script','dataLayer','GTM-PQLCXB');
|
||||
</script>
|
||||
<!-- End Google Tag Manager -->
|
||||
|
||||
<main-app></main-app>
|
||||
|
||||
<script src="app.uhk.js"></script> <!-- This should be moved to the head -->
|
||||
</body>
|
||||
</html>
|
||||
2
electron/src/main-app/index.ts
Normal file
2
electron/src/main-app/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './main-app.component';
|
||||
export * from './main-app.routes';
|
||||
67
electron/src/main-app/main-app.component.ts
Normal file
67
electron/src/main-app/main-app.component.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { Component, ViewEncapsulation, HostListener } from '@angular/core';
|
||||
|
||||
// import { Observable } from 'rxjs/Observable';
|
||||
|
||||
// import { Store } from '@ngrx/store';
|
||||
// import { AppState } from '../store';
|
||||
// import { DataStorage } from '../store/storage';
|
||||
// import { getKeymapEntities, getMacroEntities } from '../store/reducers';
|
||||
|
||||
// import { UhkBuffer } from '../config-serializer/UhkBuffer';
|
||||
// import { UserConfiguration } from '../config-serializer/config-items/UserConfiguration';
|
||||
|
||||
// import { UhkDeviceService } from '../services/uhk-device.service';
|
||||
|
||||
@Component({
|
||||
selector: 'main-app',
|
||||
template: require('../shared/main-app/main-app.component.html'),
|
||||
styles: [require('../shared/main-app/main-app.component.scss')],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class MainAppComponent {
|
||||
|
||||
// private configuration$: Observable<UserConfiguration>;
|
||||
|
||||
constructor(
|
||||
// private uhkDevice: UhkDeviceService,
|
||||
// store: Store<AppState>,
|
||||
// dataStorage: DataStorage
|
||||
) {
|
||||
// this.configuration$ = store.let(getKeymapEntities())
|
||||
// .combineLatest(store.let(getMacroEntities()))
|
||||
// .map((pair) => {
|
||||
// const config = new UserConfiguration();
|
||||
// Object.assign(config, dataStorage.getConfiguration());
|
||||
// config.keymaps = pair[0];
|
||||
// config.macros = pair[1];
|
||||
// return config;
|
||||
// });
|
||||
}
|
||||
|
||||
@HostListener('window:keydown.control.o', ['$event'])
|
||||
onCtrlO(event: KeyboardEvent): void {
|
||||
console.log('ctrl + o pressed');
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.sendConfiguration();
|
||||
}
|
||||
|
||||
private sendConfiguration(): void {
|
||||
// this.configuration$
|
||||
// .first()
|
||||
// .map(configuration => {
|
||||
// const uhkBuffer = new UhkBuffer();
|
||||
// configuration.toBinary(uhkBuffer);
|
||||
// return uhkBuffer.getBufferContent();
|
||||
// })
|
||||
// .switchMap((buffer: Buffer) => this.uhkDevice.sendConfig(buffer))
|
||||
// .do(response => console.log('Sending config finished', response))
|
||||
// .switchMap(() => this.uhkDevice.applyConfig())
|
||||
// .subscribe(
|
||||
// (response) => console.log('Applying config finished', response),
|
||||
// error => console.error('Error during uploading config', error),
|
||||
// () => console.log('Config has been sucessfully uploaded')
|
||||
// );
|
||||
}
|
||||
|
||||
}
|
||||
18
electron/src/main-app/main-app.routes.ts
Normal file
18
electron/src/main-app/main-app.routes.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { ModuleWithProviders } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { addOnRoutes } from '../shared/components/add-on';
|
||||
import { keymapRoutes } from '../components/keymap';
|
||||
import { macroRoutes } from '../shared/components/macro';
|
||||
import { settingsRoutes } from '../shared/components/settings';
|
||||
|
||||
const appRoutes: Routes = [
|
||||
...keymapRoutes,
|
||||
...macroRoutes,
|
||||
...addOnRoutes,
|
||||
...settingsRoutes
|
||||
];
|
||||
|
||||
export const appRoutingProviders: any[] = [ ];
|
||||
|
||||
export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes, { useHash: true });
|
||||
5
electron/src/main.ts
Normal file
5
electron/src/main.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
162
electron/src/services/uhk-device.service.ts
Normal file
162
electron/src/services/uhk-device.service.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Observer } from 'rxjs/Observer';
|
||||
import { ConnectableObservable } from 'rxjs/observable/ConnectableObservable';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
import { Subscriber } from 'rxjs/Subscriber';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
|
||||
import 'rxjs/add/observable/empty';
|
||||
import 'rxjs/add/observable/from';
|
||||
import 'rxjs/add/operator/catch';
|
||||
import 'rxjs/add/operator/concat';
|
||||
import 'rxjs/add/operator/concatMap';
|
||||
import 'rxjs/add/operator/mergeMap';
|
||||
import 'rxjs/add/operator/publish';
|
||||
|
||||
import { Device, Interface, InEndpoint, OutEndpoint, findByIds } from 'usb';
|
||||
|
||||
import { Layer } from '../shared/config-serializer/config-items/Layer';
|
||||
import { UhkBuffer } from '../shared/config-serializer/UhkBuffer';
|
||||
|
||||
const vendorId = 0x16d3;
|
||||
const productId = 0x05ea;
|
||||
const MAX_PAYLOAD_SIZE = 64;
|
||||
|
||||
enum Command {
|
||||
UploadConfig = 8,
|
||||
ApplyConfig = 9
|
||||
};
|
||||
|
||||
interface SenderMessage {
|
||||
buffer: Buffer;
|
||||
observer: Observer<any>;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class UhkDeviceService {
|
||||
|
||||
private device: Device;
|
||||
private connected: boolean;
|
||||
|
||||
private messageIn$: Observable<Buffer>;
|
||||
private messageOut$: Subject<SenderMessage>;
|
||||
|
||||
private outSubscription: Subscription;
|
||||
|
||||
constructor() {
|
||||
this.messageOut$ = new Subject<SenderMessage>();
|
||||
this.connect();
|
||||
}
|
||||
|
||||
connect(): void {
|
||||
if (this.connected) {
|
||||
return;
|
||||
}
|
||||
this.device = findByIds(vendorId, productId);
|
||||
if (!this.device) {
|
||||
throw new Error('UhkDevice not found.');
|
||||
}
|
||||
this.device.open();
|
||||
|
||||
const usbInterface: Interface = this.device.interface(0);
|
||||
// https://github.com/tessel/node-usb/issues/147
|
||||
// The function 'isKernelDriverActive' is not available on Windows and not even needed.
|
||||
if (process.platform !== 'win32' && usbInterface.isKernelDriverActive()) {
|
||||
usbInterface.detachKernelDriver();
|
||||
}
|
||||
usbInterface.claim();
|
||||
this.messageIn$ = Observable.create((subscriber: Subscriber<Buffer>) => {
|
||||
const inEndPoint: InEndpoint = <InEndpoint>usbInterface.endpoints[0];
|
||||
console.log('Try to read');
|
||||
inEndPoint.transfer(MAX_PAYLOAD_SIZE, (error: string, receivedBuffer: Buffer) => {
|
||||
if (error) {
|
||||
console.error('reading error', error);
|
||||
subscriber.error(error);
|
||||
} else {
|
||||
console.log('read data', receivedBuffer);
|
||||
subscriber.next(receivedBuffer);
|
||||
subscriber.complete();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const outEndPoint: OutEndpoint = <OutEndpoint>usbInterface.endpoints[1];
|
||||
const outSending = this.messageOut$.concatMap(senderPackage => {
|
||||
return (<Observable<void>>Observable.create((subscriber: Subscriber<void>) => {
|
||||
console.log('transfering', senderPackage.buffer);
|
||||
outEndPoint.transfer(senderPackage.buffer, (error) => {
|
||||
if (error) {
|
||||
console.error('transfering errored', error);
|
||||
subscriber.error(error);
|
||||
} else {
|
||||
console.log('transfering finished');
|
||||
subscriber.complete();
|
||||
}
|
||||
});
|
||||
})).concat(this.messageIn$)
|
||||
.do(buffer => senderPackage.observer.next(buffer) && senderPackage.observer.complete())
|
||||
.catch((error: string) => {
|
||||
senderPackage.observer.error(error);
|
||||
return Observable.empty<void>();
|
||||
});
|
||||
}).publish();
|
||||
this.outSubscription = outSending.connect();
|
||||
|
||||
this.connected = true;
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
if (!this.connected) {
|
||||
return;
|
||||
}
|
||||
this.outSubscription.unsubscribe();
|
||||
this.messageIn$ = undefined;
|
||||
this.device.interface(0).release();
|
||||
this.device.close();
|
||||
this.connected = false;
|
||||
}
|
||||
|
||||
sendConfig(configBuffer: Buffer): Observable<Buffer> {
|
||||
return Observable.create((subscriber: Subscriber<Buffer>) => {
|
||||
console.log('Sending...', configBuffer);
|
||||
const fragments: Buffer[] = [];
|
||||
const MAX_SENDING_PAYLOAD_SIZE = 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)]));
|
||||
}
|
||||
|
||||
const buffers: Buffer[] = [];
|
||||
const observer: Observer<Buffer> = {
|
||||
next: (buffer: Buffer) => buffers.push(buffer),
|
||||
error: error => subscriber.error(error),
|
||||
complete: () => {
|
||||
if (buffers.length === fragments.length) {
|
||||
subscriber.next(Buffer.concat(buffers));
|
||||
subscriber.complete();
|
||||
console.log('Sending finished');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fragments
|
||||
.map<SenderMessage>(fragment => ({ buffer: fragment, observer }))
|
||||
.forEach(senderPackage => this.messageOut$.next(senderPackage));
|
||||
});
|
||||
}
|
||||
|
||||
applyConfig(): Observable<Buffer> {
|
||||
return Observable.create((subscriber: Subscriber<Buffer>) => {
|
||||
console.log('Applying configuration');
|
||||
this.messageOut$.next({
|
||||
buffer: new Buffer([Command.ApplyConfig]),
|
||||
observer: subscriber
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
22
electron/src/tsconfig-electron-main.json
Normal file
22
electron/src/tsconfig-electron-main.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"removeComments": false,
|
||||
"noImplicitAny": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"typeRoots": [
|
||||
"../../node_modules/@types"
|
||||
],
|
||||
"types": [
|
||||
"electron"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"electron-main.ts"
|
||||
]
|
||||
}
|
||||
29
electron/src/tsconfig.json
Normal file
29
electron/src/tsconfig.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"removeComments": false,
|
||||
"noImplicitAny": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"typeRoots": [
|
||||
"../../node_modules/@types"
|
||||
],
|
||||
"types": [
|
||||
"node",
|
||||
"jquery",
|
||||
"core-js",
|
||||
"select2",
|
||||
"electron",
|
||||
"usb"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"../dist",
|
||||
"electron-main.ts",
|
||||
"webpack.config.*"
|
||||
]
|
||||
}
|
||||
32
electron/src/webpack.config.electron-main.js
Normal file
32
electron/src/webpack.config.electron-main.js
Normal file
@@ -0,0 +1,32 @@
|
||||
//var webpack = require("webpack");
|
||||
var webpackFailPlugin = require('webpack-fail-plugin');
|
||||
var path = require('path');
|
||||
|
||||
var rootDir = path.resolve(__dirname, '../');
|
||||
|
||||
module.exports = {
|
||||
entry: [ path.resolve(rootDir, 'src/electron-main.ts')],
|
||||
output: {
|
||||
path: rootDir + "/dist",
|
||||
filename: "electron-main.js"
|
||||
},
|
||||
target: 'electron-main',
|
||||
devtool: 'source-map',
|
||||
resolve: {
|
||||
extensions: ['', '.webpack.js', '.web.js', '.ts', '.js'],
|
||||
modules: [path.join(rootDir, "node_modules")]
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{ test: /\.ts$/, loader: 'ts-loader?' + JSON.stringify({ configFileName: 'tsconfig-electron-main.json' }), exclude: /node_modules/ },
|
||||
]},
|
||||
plugins: [
|
||||
// new webpack.optimize.UglifyJsPlugin({ minimize: true })
|
||||
webpackFailPlugin
|
||||
],
|
||||
node: {
|
||||
__dirname: false,
|
||||
__filename: false
|
||||
}
|
||||
|
||||
}
|
||||
94
electron/src/webpack.config.js
Normal file
94
electron/src/webpack.config.js
Normal file
@@ -0,0 +1,94 @@
|
||||
var webpack = require("webpack");
|
||||
var SvgStore = require('webpack-svgstore-plugin');
|
||||
var webpackFailPlugin = require('webpack-fail-plugin');
|
||||
var CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
var path = require('path');
|
||||
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
|
||||
|
||||
var rootDir = path.resolve(__dirname, '../');
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
polyfills: path.resolve(rootDir, 'src/shared/polyfills.ts'),
|
||||
vendor: path.resolve(rootDir, 'src/shared/vendor.ts'),
|
||||
app: path.resolve(rootDir, 'src/main.ts')
|
||||
},
|
||||
output: {
|
||||
path: rootDir + "/dist",
|
||||
filename: "[name].uhk.js"
|
||||
},
|
||||
target: 'electron-renderer',
|
||||
externals: {
|
||||
usb: 'usb'
|
||||
},
|
||||
devtool: 'source-map',
|
||||
resolve: {
|
||||
extensions: ['', '.webpack.js', '.web.js', '.ts', '.js'],
|
||||
modules: [path.join(rootDir, "node_modules")],
|
||||
alias: {
|
||||
jquery: 'jquery/dist/jquery.min.js',
|
||||
select2: 'select2/dist/js/select2.full.min.js'
|
||||
}
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{ test: /\.ts$/, loader: 'ts-loader', exclude: /node_modules/ },
|
||||
{ test: /\.html$/, loader: 'html-loader?attrs=false' },
|
||||
{
|
||||
test: /\.scss$/,
|
||||
exclude: /node_modules/,
|
||||
loaders: ['raw-loader', 'sass-loader']
|
||||
},
|
||||
{ test: /jquery/, loader: 'expose?$!expose?jQuery' },
|
||||
{ test: require.resolve("usb"), loader: "expose?usb" }
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
// new webpack.optimize.UglifyJsPlugin({ minimize: true })
|
||||
new SvgStore({
|
||||
svgoOptions: {
|
||||
plugins: [
|
||||
{ removeTitle: true }
|
||||
]
|
||||
}
|
||||
}),
|
||||
webpackFailPlugin,
|
||||
new CopyWebpackPlugin(
|
||||
[
|
||||
{ from: './electron/src/**/*.html', flatten: true },
|
||||
{ from: './electron/src/**/*.js', flatten: true },
|
||||
{
|
||||
from: 'node_modules/font-awesome/css/font-awesome.min.css',
|
||||
to: 'vendor/font-awesome/css/font-awesome.min.css'
|
||||
},
|
||||
{
|
||||
from: 'node_modules/font-awesome/fonts',
|
||||
to: 'vendor/font-awesome/fonts'
|
||||
},
|
||||
{
|
||||
from: 'node_modules/bootstrap/dist/',
|
||||
to: 'vendor/bootstrap'
|
||||
},
|
||||
{
|
||||
from: 'images',
|
||||
to: 'images'
|
||||
},
|
||||
{
|
||||
from: 'node_modules/usb',
|
||||
to: 'vendor/usb'
|
||||
}
|
||||
],
|
||||
{
|
||||
ignore: ['*.config.js']
|
||||
}
|
||||
),
|
||||
new webpack.ProvidePlugin({
|
||||
$: "jquery",
|
||||
jQuery: "jquery"
|
||||
}),
|
||||
new CommonsChunkPlugin({
|
||||
name: ['app', 'vendor', 'polyfills']
|
||||
})
|
||||
]
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user