427 Commits

Author SHA1 Message Date
Róbert Kiss
9edf114b71 chore(usb): add kboot-firmware-upgrade script 2019-07-31 06:11:29 +02:00
Róbert Kiss
4d2d6d40fb chore(kboot): add more logging (#1004) 2019-07-30 22:23:37 +02:00
Róbert Kiss
db2e14a852 chore: upgrade ts-node => 8.3.0 (#1003) 2019-07-29 20:56:01 +02:00
Róbert Kiss
757a201c47 chore: upgrade lerna => 3.16.4 (#998) 2019-07-28 23:30:35 +02:00
Róbert Kiss
c2c997c9b0 feat: decrease the size between the keyboard layout and description in split mode (#990)
* feat: decrease the size between the keyboard layout and description if split

* fix: increase the distance between the keyboard and keyboard desc.

* fix: increase the description distance in split mode
2019-07-28 20:43:12 +02:00
Róbert Kiss
0015e6deb3 chore: upgrade electron => 4.2.8 (#995)
Summary:
- 10.11.0 the bundled Node.js version in electron
- Chromium version number 69.0.3497.128

BREAKING CHANGES:
- require minimum MacOs version 10.10 Yosemite
2019-07-28 20:41:11 +02:00
Róbert Kiss
1134c1d16e chore: write kboot logs in development mode (#994) 2019-07-28 12:50:44 +02:00
Róbert Kiss
1b569aa82b chore: use fs-extra instead of mkdirp package (#992) 2019-07-28 12:32:30 +02:00
Róbert Kiss
66d6802920 chore: upgrade typescript => 3.5.3 (#993) 2019-07-28 12:31:37 +02:00
Róbert Kiss
bf1689dd34 chore: upgrade fs-extra => 8.1.0 (#989) 2019-07-26 12:33:43 +02:00
Róbert Kiss
f5e05f19b6 chore: upgrade ts-lib => 1.10.0 (#991) 2019-07-26 12:33:06 +02:00
Róbert Kiss
05b8fc89fe chore: remove autoprefixer
We don't use the package directly.
The @angular-devkit/build-angular@0.13.9 use the up to date version and
manage the css prefixing
2019-07-26 06:21:30 +02:00
Róbert Kiss
cbccaba1c5 feat: Display/hide left keyboard half and merged/unmerged state (#987)
* feat: Display/hide left keyboard half and merged/unmerged state

* feat: improve the animation

* feat: decrease left fade animation time
2019-07-25 23:41:20 +02:00
Róbert Kiss
a409c219d8 feat: Provide reasonable default mouse settings for Macs (#982)
* feat: Provide reasonable default mouse settings for Macs

* fix: hack to eliminate the double scrollbar
2019-07-25 22:02:52 +02:00
Róbert Kiss
2b84101975 chore: upgrade karma-coverage-istanbul-reporter => 2.0.6 (#986)
The new version uses newer version of the `handlebars` and it
protected `Prototype Pollution` vulnerability.
2019-07-23 18:41:13 +02:00
Róbert Kiss
1c551c0669 chore: upgrade tslint => 5.18.0 (#985) 2019-07-23 00:46:51 +02:00
Róbert Kiss
fe24ebbefc chore: upgrade lerna => 3.16.1 2019-07-23 00:22:51 +02:00
Róbert Kiss
a4cb4b7e0c style: remove extra space in user config reducer (#983) 2019-07-22 23:15:45 +02:00
Róbert Kiss
cba93a41e0 chore: upgrade lodash => 4.17.15 (#981) 2019-07-22 21:29:07 +02:00
Eric Tang
9652152225 Sign CLA (#979) 2019-07-21 23:07:37 +02:00
László Monda
ffcfce04d5 Make scripts terminate with a non-zero error code upon failure. 2019-07-20 13:04:11 +02:00
László Monda
7c6da6c4e6 Fix changelog. 2019-07-14 12:50:04 +02:00
László Monda
4c57f28a3c Update frimware version to 8.5.4 2019-07-14 12:48:38 +02:00
László Monda
4677829d56 Update changelog. 2019-07-14 12:45:16 +02:00
László Monda
e812b7dfcc Make the help page a bit tighter. 2019-07-14 12:44:47 +02:00
Róbert Kiss
0290dee35c chore: the correct windows code sign certificate 2019-07-07 19:47:38 +02:00
Sam Rang
3c6c12d7a8 Sign CLA (#977) 2019-07-05 21:16:50 +02:00
Róbert Kiss
01aa9f119c chore: update windows code sign certificate (#976) 2019-07-03 22:54:37 +02:00
Kristian Sloth Lauszus
2850658055 Sign CLA (#975) 2019-07-02 02:44:32 +02:00
Spencer Owen
c1e295c92d I have read the Agreement, and fully agree to it by signing it with my GitHub username. (#964) 2019-06-22 09:10:40 +02:00
Mikko Lakomaa
40d29886cb Sign CLA (#971) 2019-06-18 22:24:19 +02:00
Árpád Csányi
a02c4deae1 Sign CLA. (#972) 2019-06-18 22:11:39 +02:00
József Farkas
c1b7078549 Sign CLA (#970) 2019-06-13 22:50:43 +02:00
László Monda
293affacb3 Sort signatures alphabetically. 2019-06-13 11:00:40 +02:00
dgyimesi
c41450d0dd Sign CLA. (#969) 2019-06-13 10:57:03 +02:00
László Monda
a9ec8efb2b Sort signatures alphabetically. 2019-06-13 00:37:32 +02:00
Tim Coker
d7ecd5a083 sign-cla (#968) 2019-06-13 00:36:07 +02:00
László Monda
8c4b13cd94 Sort names alphabetically. 2019-06-12 20:12:33 +02:00
Róbert Kiss
b4ec69ff6d chore: ert78gb sign cla (#966) 2019-06-12 20:11:52 +02:00
László Monda
62f69b5f70 Alphabetically sort signatures. 2019-06-12 17:59:03 +02:00
Attila Csanyi
e2fda9201f Sign CLA (#965)
I have read the Agreement, and fully agree to it by signing it with my GitHub username.
2019-06-12 17:58:18 +02:00
Nejc Zdovc
bf9bfb8d82 CLA Nejc Zdovc (#963) 2019-06-10 12:43:15 +02:00
László Monda
02089d166c Update CONTRIBUTING.md 2019-06-07 21:43:56 +02:00
László Monda
3df6e8214b Update CONTRIBUTING.md 2019-06-07 21:41:55 +02:00
László Monda
fbc8970baa Update CONTRIBUTING.md 2019-06-07 21:40:13 +02:00
László Monda
88b3b2fb65 Sign CLA. (#962) 2019-06-07 21:31:09 +02:00
László Monda
41f73acfce Add CLA. 2019-06-07 21:20:00 +02:00
Róbert Kiss
9d41f40d93 chore: use the correct license entry in the package.json of the uhk-web (#960) 2019-06-02 22:21:29 +02:00
Róbert Kiss
56ebfba8ad chore: move @types/* dependencies to the root package.json (#961) 2019-06-02 22:21:05 +02:00
Róbert Kiss
400f01dabe chore: upgrade @angular/cli => 7.3.9 and devkit => 0.13.9 (#959) 2019-05-31 12:36:27 +02:00
Róbert Kiss
c64515885b chore: upgrade electron => 3.1.10 (#958) 2019-05-30 01:35:18 +02:00
Róbert Kiss
82c9126d82 refactor: use Buffer.from and Buffer.alloc instead of new Buffer() (#957) 2019-05-29 21:56:01 +02:00
Róbert Kiss
999feea488 chore: upgrade angular => 7.2.15 and ngrx => 7.4.0 (#956) 2019-05-29 20:28:29 +02:00
László Monda
4dd2400d36 Add a 1s delay to updateDeviceFirmware() 2019-05-23 01:26:32 +02:00
László Monda
5657579d6d Add a 1s delay to updateModuleFirmware() 2019-05-23 01:10:54 +02:00
Róbert Kiss
58b83f2b6b chore: upgrade jasmine* => 3.4.0 (#953) 2019-05-23 00:35:53 +02:00
Róbert Kiss
b991e2c9d4 chore: upgrade lerna => 3.14.1 (#952) 2019-05-23 00:00:45 +02:00
Róbert Kiss
e6495ded05 chore: upgrade node-hid => 0.7.8 (#951) 2019-05-22 23:27:40 +02:00
László Monda
21cab7e755 Revert "Rebuild node-hid for the usb repo to avoid "dev 1d50 / 6122 : usage_page:312d, usage:2e30" messages when using USB scripts."
This reverts commit 2dfb61a38e.
2019-05-22 21:23:23 +02:00
László Monda
2dfb61a38e Rebuild node-hid for the usb repo to avoid "dev 1d50 / 6122 : usage_page:312d, usage:2e30" messages when using USB scripts. 2019-05-22 18:09:36 +02:00
László Monda
2c93975c77 Rename {left,right}-addon to {left,right}-module 2019-05-15 23:41:42 +02:00
László Monda
427f706287 Rename {left,right}Addon to {left,right}Module 2019-05-15 23:35:05 +02:00
Róbert Kiss
1b59d96296 fix: don't show the udev setup info if udev v2 setup available (#943) 2019-04-30 03:05:03 +02:00
Róbert Kiss
20e2c8bbbc chore: upgrade jQuery => 3.4.0
fix: https://nvd.nist.gov/vuln/detail/CVE-2019-11358
2019-04-28 17:06:59 +02:00
László Monda
c2f47f18b0 Link the UHK knowledgebase from the help page. 2019-04-24 17:00:15 +02:00
Tim Coker
1ee31003fd fix for middle clicking links (#937)
* fix for middle clicking links

* Found another link in the app

* Converted all links to use externaUrl decorator

* Missing semicolon

* Minor fixes for compilation/linting

* Changed help link back to embedded in html

* trigger builds

* trigger build for agreement removal
2019-04-24 16:09:32 +02:00
Róbert Kiss
4abdac1ef6 fix: delete LayerSwitcher on the dest layer when LayerSwitcher remove/changing (#939) 2019-04-18 01:58:16 +02:00
Róbert Kiss
32db3817e2 chore: upgrade stylelint to 10.0.0 (#938)
* chore: upgrade stylelint to 10.0.0

* Trigger build
2019-04-16 22:11:11 +02:00
Róbert Kiss
264752530f chore: upgrade bootstrap to 3.4.1 (#928)
* chore: upgrade bootstrap to 3.4.1

* fix: add whitelist to the toolbox sanitizer

* fix: remove unnecessary console.log
2019-04-16 21:02:13 +02:00
Róbert Kiss
33c910d70c chore: upgrade angular => 7.x (#925)
* Merge branch 'master' into chore-upgrade-angular-to-7

* reformat files of the store

* set preserveWhitespaces = true

* delete console.log from AutoGrowInputComponent

* fix null pinter exception when set the keyaction on an undefined key

* speed tuning

* delete svg-keyboard-key animation

* revert electron logger upgrade

* improve animation speed of scg-keyboard-key component

* fix: popover keymap tab visibility

* fix: remove btn-line css class
2019-03-04 10:27:25 +01:00
Róbert Kiss
b1b2f1d431 chore: remove standard-version from devDependencies (#924)
We don't use standard-version to generate changelog
2019-02-28 19:16:27 +01:00
Skye Jensen
c3a6b373d4 Update lerna dependency from 3.2.0 to 3.13.0 (#923) 2019-02-27 21:11:42 +01:00
Róbert Kiss
f94b221ee9 feat: convert UdevRulesInfo to string enum (#921)
* feat: convert UdevRulesInfo to string enum

For easier log reading better to use string enum.

* fix typo
2019-02-24 22:04:46 +01:00
Róbert Kiss
3a1451a6a9 fix: agent falsely reports UHK detected (#922)
close #915
2019-02-24 19:48:36 +01:00
Róbert Kiss
7a2c8cb2e4 refactor: ngrx-store use enum ActionTypes and Action classes everywhere (#919) 2019-02-22 19:00:17 +01:00
Róbert Kiss
de71f6f88c chore: upgrade lodash (#914) 2019-02-21 11:18:13 +01:00
Róbert Kiss
5fd555c214 refactor: remove console.log from popover component (#911) 2019-02-02 02:12:20 +01:00
Róbert Kiss
9a0b0f9df1 feat: redesign auto-update component (#910) 2019-02-02 02:11:54 +01:00
Róbert Kiss
12d361eb2e feat: save window state when closing agent (#902) 2019-01-30 02:47:23 +01:00
dgyimesi
2c041b64ff feat: Redesign About page. (#899)
* Redesign About page.

* feat: migrate About page to ngrx.

* Fix import relative path; order contributors by the number of contributions.
2019-01-27 00:43:57 +01:00
dgyimesi
8947f2dedf feat: Make the Agent application icon slightly smaller. (#898)
* feat: Make the Agent application icon slightly smaller.

* feat: generate icon.icns
2019-01-26 22:51:13 +01:00
Róbert Kiss
6b4cf41357 chore: upgrade gh-pages => 2.0.1 (#907)
fix: https://nodesecurity.io/advisories/658
2019-01-26 21:46:56 +01:00
dgyimesi
c36156b497 feat: Make Right click, Esc set the key action to none (#897)
* feat: Make Right click, Esc set the key action to none

* refactor: use ts-keycode-enum; revert: escape from mapping on pressing escape.
2019-01-24 14:25:22 +01:00
Róbert Kiss
d674fd4490 chore: upgrade electron => 2.0.16 (#903) 2019-01-24 03:22:12 +01:00
Róbert Kiss
8acc33e719 chore: set preserveWhitespaces: false in angular tsconfig.json (#904) 2019-01-24 01:41:52 +01:00
Mónus Tamás
2855480d6a fix: Pressing Tab after "update description" corrupts UI (#872) (#878)
* fix: Pressing Tab after "update description" corrupts UI (#872)

* Prevent focus on button groups by setting tabindex to -1.
2019-01-21 20:09:06 +01:00
László Monda
eee3322082 Use TAG+="uaccess" instead of MODE:="0666", GROUP="plugdev" in the udev rules for better compatibility. Fixes #901 2019-01-21 14:47:17 +01:00
Róbert Kiss
bb31c2cefa refactor: use rxjs pipe syntax (#900)
The `let` operator was not migrated because earlier two reducer needed be refactored
- user-configuration reducer
- present reducer

This commit is prerequisite of the angular upgrade.
2019-01-20 23:23:01 +01:00
dgyimesi
e18a98d8bb fix: Don't change the tab immediately when closing the key action popover (#895) 2019-01-20 22:55:06 +01:00
Róbert Kiss
3964698cf7 feat: kboot package (#894)
* feat: kboot package

* feat: kboot package

* fix: wait 1 sec after device is available

* test: fix unit test

* refactor: clean unused codes

* doc: improve readme.md

* doc: improve readme.md

* test: fix unit test

* chore: fix lint settings

* style: fix linting issues
2019-01-18 17:37:31 +01:00
Róbert Kiss
bfc08edfce chore(release): 1.2.13 2019-01-07 21:56:23 +01:00
Róbert Kiss
6e2115ac74 feat: kboot package (#889) 2019-01-07 21:36:36 +01:00
Róbert Kiss
1eb8720305 chore: reorganize npm script (#884)
The goal is every package should be responsible to `lint`, `build` and
`test` commands. This modification helps to easier add new package to
the repository.
2018-12-22 02:20:38 +01:00
Róbert Kiss
c9f052b8c7 chore: delete unused webpack.config.js file in test-serializer package (#882) 2018-12-22 00:52:43 +01:00
Róbert Kiss
58ee42fcc2 chore: unify tsconfig.json files (#883) 2018-12-22 00:46:17 +01:00
Róbert Kiss
9112b597f8 fix: device recovery mode (#879) 2018-12-18 00:10:02 +01:00
Róbert Kiss
8c7d625573 build: set publisherName for win release 2018-11-15 22:33:22 +01:00
Róbert Kiss
52a57c0e87 build: upgrade electron-builder and remove unsupported appMetadata (#860) 2018-11-14 23:40:28 +01:00
Róbert Kiss
a52b34fc3f Revert "chore: rebuild node-hid to hide USB usage data (#853)" (#861)
This reverts commit 1a14ac020e.
2018-11-14 23:14:05 +01:00
Róbert Kiss
1a14ac020e chore: rebuild node-hid to hide USB usage data (#853) 2018-11-14 22:22:24 +01:00
László Monda
330f7e72be Bump Agent version to 1.2.12 and update changelog. 2018-11-14 19:57:35 +01:00
Róbert Kiss
1ed6669ced fix: compare available devices by vid, pid, interface on linux (#854) 2018-11-13 21:44:22 +01:00
Róbert Kiss
108d60a497 feat: show os specific modifiers for macro actions (#855) 2018-11-12 23:22:26 +01:00
Róbert Kiss
1a9bd7de83 feat: show udev rules on missing device screen (#842)
* feat: show udev rules on missing device screen

* delete MissingDeviceComponent

* feat: change privilege and message device screen texts

* feat: change message device screen texts

* fix: load config from device

* fix: load config when hasPermission = true

* fix: load app start info after set permission rules

* fix: action dispatch

* fix: load config from device when UdevRulesInfo.Ok

* fix: load config from device when 0 device available

* feat: add extra space between the "old udev rule" and permission button
2018-11-09 01:30:40 +01:00
Róbert Kiss
10cd06c70b feat: allow 8.4.3 firmware features (#845)
* feat: allow 8.4.3 firmware features

* feat: remove "layer-double-tap" command line argument
2018-11-05 19:51:39 +01:00
László Monda
84b6c33c54 Upgrade to firmware 8.5.3 2018-10-30 04:02:24 +01:00
Róbert Kiss
18808eae9c fix: user Renderer2 instead of deprecated Renderer (#844) 2018-10-30 03:06:44 +01:00
Róbert Kiss
ca74b0a76b feat: log USB device list before check permission (#837) 2018-10-22 21:32:16 +02:00
Róbert Kiss
b45d60efb2 feat: log USB device list before check permission (#837) 2018-10-22 07:34:14 +02:00
Richard Prillwitz
a4e3696078 chore: upgrade dependencies (#828)
* fixed vulnerabilities;

* update lerna from 3.1.4 to 3.2.0;

* fixed linter complaining about two empty lines in scss file;

* use fixed versions,
2018-10-16 19:17:12 +02:00
Róbert Kiss
2b963993d2 fix: stop event propagation when capturing keypress action (#817)
* fix: stop event propagation when capturing keypress action

* fix: save to keyboard shortcut allowed when keypress not capturing
2018-10-14 20:43:35 +02:00
László Monda
e333022043 Add international scancodes. Fixes #807. 2018-10-09 23:02:07 +02:00
Róbert Kiss
2e2a59ccb8 feat: remove Stop/Eject keypress action (#819)
* feat: remove Stop/Eject keypress action

* feat: remove WWW and Launch Web Browser keypress actions
2018-10-08 02:35:48 +02:00
Róbert Kiss
6f073ad718 feat: use icon for play/pause keypress action (#818)
* feat: use icon for play/pause keypress action

* fix: use rectangle layout in svg-single-icon-key
2018-10-08 02:32:02 +02:00
László Monda
166834e46c Update udev rules based on the instructions of https://github.com/node-hid/node-hid/releases/tag/v0.7.2 2018-10-08 00:22:14 +02:00
László Monda
0ff2364b9e Update from firmware 8.2.5 to 8.5.2 2018-10-08 00:00:08 +02:00
Róbert Kiss
2cbfc6a11e fix: ui break when press tab key in keymap short name control (#816) 2018-10-07 23:21:25 +02:00
Róbert Kiss
404ccc7b2b chore: use default electron menu on mac in development mode (#815) 2018-10-07 22:42:39 +02:00
Róbert Kiss
42e413ab65 chore: upgrade node-hid => 0.7.3 (#814) 2018-10-07 22:29:10 +02:00
Róbert Kiss
d57ef66038 feat: disable other popover tabs if the current key is layer switcher action (#813) 2018-10-07 22:14:36 +02:00
Róbert Kiss
843b4cbf68 feat: add "Edit" menu to MacOs build (#812) 2018-10-07 21:02:28 +02:00
Róbert Kiss
7e4b7c5c8b fix: macro action text overlap (#811) 2018-10-07 18:31:15 +02:00
László Monda
2ff65537a0 Update ISSUE_TEMPLATE 2018-10-06 20:28:13 +02:00
László Monda
6e2b1fb18d Add usbReportSemaphore to uhk.variableNametoId 2018-10-04 19:27:19 +02:00
László Monda
3e621a2818 Bump version to 1.2.11 and update package.json 2018-10-03 05:43:41 +02:00
Róbert Kiss
247ec4c1b2 feat: add backspace and caps lock icons (#803)
Summary:
- svg sprite generate with xmlns:xlink="http://www.w3.org/1999/xlink" namespace
- uhk-icon-agent-icon css class renamed to uhk-icon-pure-agent-icon because
  it collided with agent icon sprites
- added backspace and caps lock icons
2018-10-01 02:06:23 +02:00
Róbert Kiss
edcff069fd fix: write agent version into the log when upgrade firmware (#802) 2018-09-30 22:48:42 +02:00
László Monda
8afdeac306 Fix right and middle mouse click macro actions which were exchanged. Fixes #794. 2018-09-27 00:35:13 +02:00
László Monda
425f861451 Add issue template regarding Karabiner Elements. 2018-09-26 00:05:14 +02:00
László Monda
5a843ed02c Include Agent version to the firmware update log. 2018-09-25 15:48:27 +02:00
László Monda
f3bd83af03 Bump version to 1.2.10, update changelog and package.json 2018-09-24 04:21:24 +02:00
László Monda
0b3fca63b7 Add History Back and History Forward scancodes. Resolves #776. 2018-09-24 03:56:29 +02:00
László Monda
cbd4460df0 Map Caps Lock without Ctrl on default keymaps. 2018-09-24 03:03:05 +02:00
László Monda
b941bd9a75 Set the decelerated scroll speed of the default configuration to 10. 2018-09-24 02:32:25 +02:00
László Monda
439b84affc Save decelerated mouse scroll speed properly instead of using the accelerated scroll speed by accident. Fixes https://github.com/UltimateHackingKeyboard/firmware/issues/178 2018-09-24 02:21:00 +02:00
László Monda
66d5302e6f Add play/pause icon. Improves #591. 2018-09-21 01:43:15 +02:00
László Monda
9e2e2b9c5c Add more exact instructions to the permission setup screen. Fixes #781. 2018-09-21 00:56:50 +02:00
Róbert Kiss
aa243ac7b0 feat: allow Layer Switcher secondary roles only on base layer (#790) 2018-09-21 00:20:46 +02:00
László Monda
eb421e0681 Revert "Remove unused mustache file."
This reverts commit 63aae8f578.
2018-09-20 02:31:06 +02:00
László Monda
63aae8f578 Remove unused mustache file. 2018-09-20 02:08:58 +02:00
László Monda
e577454a31 Make all SVGs non-executable. 2018-09-20 02:05:51 +02:00
László Monda
e802bb0052 Remove unused SVGs. 2018-09-20 02:05:15 +02:00
László Monda
b98e5df20a Add backspace and caps lock icons. Improves #83. 2018-09-20 01:42:05 +02:00
Róbert Kiss
7332105edb feat: remap on all keymap warning (#789) 2018-09-19 23:02:32 +02:00
László Monda
6a4feaf18d Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2018-09-16 21:30:58 +02:00
László Monda
ee637d7958 Rename Scroll Lock to ScrLk and Num Lock to NumLk on keys. Improves #83. 2018-09-16 21:28:54 +02:00
Róbert Kiss
8d161ce8ff chore: upgrade Node.js => 8.12.0 (#783)
it's fix appveyor build
2018-09-16 21:17:40 +02:00
László Monda
8010bd8195 In the scancode select2 display "Print Screen SysRq" and add SysRq above PrtScn when rendering the key. Fixes #784. 2018-09-16 21:09:54 +02:00
Jan Christoph Ebersbach
059f1d5505 Fix swapped direction title for mouse movements (#778) 2018-09-16 01:16:11 +02:00
László Monda
123cab5724 Bump Agent version to 1.2.9 2018-09-13 20:42:07 +02:00
Róbert Kiss
c16365a0e5 fix: Alt+Tab triggers "Remap on all layers" (#773) 2018-09-11 22:29:30 +02:00
Róbert Kiss
a21d278c0c fix: modifier button layout (#772) 2018-09-11 21:56:30 +02:00
Róbert Kiss
0466916be1 fix: key modifier reordering (#771) 2018-09-11 00:29:22 +02:00
László Monda
9a845d8f6a Fix eeprom.js 2018-09-10 19:29:58 +02:00
Róbert Kiss
9ae1673499 feat: secondary role visualisation (#767)
* feat: secondary role visualisation

* fix: recalculate the text position of the secondary role if changes

* fix: recalculate the text position of SvgKeystrokeKeyComponent

* fix: recalculate the text position when changes anything

* fix: two line text key position calculation

* fix: fix space positioning

* fix: visualize second character of complex key

* style: remove extra line
2018-09-08 12:09:16 +02:00
László Monda
2d5a5e7aef Exchange Alt and Super modifiers in the key action popover. 2018-09-03 03:58:03 +02:00
Róbert Kiss
3e4d439852 feat: display OS-specific modifiers (#764)
* chore: git ignore "out-tsc/" folder in uhk-web package

* feat: add OperationSystem calculation to the app reducer

* feat: create os specific key modifier

* feat: Os specific texts

* revert: KeyModifierValues and getKeyModifiers selector

* refactor: remove unnecessary return

* refactor: rename OperationSystem => OperatingSystem
2018-09-03 00:21:55 +02:00
László Monda
aba0b09109 Bump Agent to version 1.2.8 and update changelog. 2018-08-26 23:42:31 +02:00
László Monda
af608ee17d Downgrade to firmware 8.2.5 2018-08-26 23:35:54 +02:00
Róbert Kiss
df817e86d6 fix: use OnPush change detection on the keymap edit and add pages (#760)
* fix: use OnPush change detection on the keymap edit and add pages

* fix: font-size
2018-08-26 22:08:29 +02:00
Róbert Kiss
a7d3b62512 chore: upgrade many dependencies and restructure tsconfigs (#757)
* chore: upgrade many dependencies and restructure tsconfigs

Summary:
- upgrade many dependencies
- remove dev dependencies from test-serializer and uhk-common
- created root tsconfig.json and test-serializer and uhk-common
  tsconfigs extends it
- fixed e2e test

* chore: upgrade more dependencies
2018-08-22 00:34:16 +02:00
Róbert Kiss
b41f14192a chore: upgrade stylelint => 9.5.0 (#756)
it is fix "Unknown CSS word" error in IntelliJ
2018-08-21 20:43:18 +02:00
Róbert Kiss
475ec71983 fix: uncheck 'Remap on all keyboard' and 'Remap on all layer' checkbox by default (#754)
* fix: uncheck 'Remap on all keyboard' and 'Remap on all layer' checkbox by default

* fix: popover checkbox checked state
2018-08-21 20:41:54 +02:00
László Monda
b5cff2fa93 Fix the padding of the secondary role tooltip. 2018-08-14 04:10:30 +02:00
László Monda
80e8c014ec Bind left and right Shift on the Mouse layer of all keymaps in the default configuration. 2018-08-08 17:00:32 +02:00
László Monda
67d42f666c Simplify the output of get-debug-info.js 2018-08-04 02:29:39 +02:00
László Monda
fa32f95438 Add note to the LED brightness page saying that current UHK versions are not backlit. Also include spacing-bootstrap-3 2018-07-30 00:05:04 +02:00
László Monda
06878dd56a Remove the redundant scrollbar from the LED brightness page. 2018-07-29 00:28:34 +02:00
Róbert Kiss
374f6a3e6e chore: downgrade electron builder => 20.14.7 (#741)
* chore: downgrade electron builder => 20.14.7

* chore: downgrade electron builder => 20.8.1
2018-07-26 23:30:20 +02:00
László Monda
0b9c804a3d Bump Agent version to 1.2.7 and update changelog. 2018-07-26 22:20:28 +02:00
László Monda
365a459d61 Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2018-07-26 22:16:33 +02:00
László Monda
cec891a2c0 Change the shortcut which enables the USB stack test code, so that it can be triggered with the default Mac US keymap. 2018-07-26 22:15:21 +02:00
Róbert Kiss
8eb8aa3032 chore: upgrade electron builder => 20.26.0 (#740) 2018-07-26 22:06:32 +02:00
László Monda
38184e7968 Bump Agent version to 1.2.6 and firmware version to 8.4.0. Update changelog. 2018-07-26 06:04:47 +02:00
Róbert Kiss
f6092ea195 fix: popover components use OnPush change detection (#727)
* fix: popover components use OnPush change detection

* fix: select2 selection bug

* chore: upgrade @ert78gb/ngx-select-ex => 3.7.0
2018-07-26 05:37:58 +02:00
László Monda
ac7d66e338 Add keyboard shortcut for enabling the USB stack test mode of the firmware. Resolves #735. 2018-07-26 05:34:48 +02:00
László Monda
b82a1da92a Remove usbVariables in favor of variableNameToId. 2018-07-25 11:44:57 +02:00
László Monda
b8859f7b64 Make {get,set}-variable.js expect variable name and use async/await. 2018-07-25 02:08:36 +02:00
László Monda
a04fa67446 Comment out the Settings menu until auto update is implemented. 2018-07-24 16:40:34 +02:00
Eric Tang
ac89aff018 Add scripts for getting/setting firmware variables (#734) 2018-07-22 16:07:21 +02:00
Róbert Kiss
e7cf8dc966 fix: no scroll when macro tab selected on popover (#731) 2018-07-17 22:08:45 +02:00
Róbert Kiss
d0102f5bdb feat: add help page (#728)
* feat: add help page

* feat: add help page content
2018-07-16 23:05:41 +02:00
László Monda
eb0daadf98 Overwrite the blhost binary with a statically compiled version that doesn't use special instructions.
See https://github.com/UltimateHackingKeyboard/agent/issues/681#issuecomment-403057795
2018-07-14 14:43:11 +02:00
László Monda
49d6ca173d Remove the blhost-old x86-64 Linux blhost binary. 2018-07-14 14:41:53 +02:00
László Monda
a3eb6a6b7e Fix typo. 2018-07-11 14:48:28 +02:00
László Monda
144ed57b20 Fix epic typo. 2018-07-08 14:44:41 +02:00
Róbert Kiss
6086ddabf0 build: build only AppImage for Linux (#719)
upgrade:
 - electron-builder => 20.15.0
 - electron-log => 2.2.16
 - electron-rebuild => 1.8.1
2018-07-08 14:34:38 +02:00
Róbert Kiss
84f378a276 feat: add save to keyboard and remap shortcut keys (#712)
* feat: add save to keyboard and remap shortcut keys

* feat: Alt and Shift keys set the remapOnAllKeymap and remapOnAllLayer

* fix: control + enter trigger remap keymap
2018-07-08 14:31:48 +02:00
Róbert Kiss
648e8d5f2c feat: keep current selected layer when changing keymap (#714) 2018-07-05 23:58:44 +02:00
Róbert Kiss
15df8d7129 WIP feat: replace ng2-select2 => ngx-select-ex (#706)
* feat: replace ng2-select2 => ngx-select-ex

* feat: style the ngrx-select

* feat: replace secondary role select2

* feat: replace Select2OptionData => SelectOptionData

* feat: replace select2 => ngx-select in macro-tab component

* feat: replace select2 => ngx-select in keymap-tab component

* feat: fix styles

* chore: remove select2 from dependencies

* fix: macro editor overflow

* fix: set the same font size for the toggle button

* fix: overflow

* chore: use @ert78gb/ngx-select-ex version of ngx-select-ex
2018-07-02 23:44:39 +02:00
Sylvain Benner
cfc0af9655 Fix Sleep key for macOS users in Mac keymaps and Fn layers (#707)
Reference: https://support.apple.com/en-us/HT201236
2018-06-29 12:19:59 +02:00
László Monda
f02e3181a6 Improve the phrasing of the firmware update error message. 2018-06-28 18:49:01 +02:00
Róbert Kiss
3d59bcf97e feat: Tweak unsupported Windows firmware update notification (#705)
* feat: Tweak unsupported Windows firmware update notification

* feat: Display firmware update status

* feat: throw error when left half not connected under firmware upgrade
2018-06-28 18:41:16 +02:00
László Monda
5e4fc983fb Rename the "Remap Key" button to "Remay key". 2018-06-27 21:28:16 +02:00
László Monda
32d9635b34 Tone down the color of the separator line. 2018-06-26 06:05:03 +02:00
László Monda
3978011d2e Bump Agent version to 1.2.5 and update changelog. 2018-06-26 03:13:49 +02:00
László Monda
cd1952a7df Restore blhost as blhost.old from 0f24427628 2018-06-26 02:21:33 +02:00
László Monda
4251477451 Comment out the export keymap icon because there isn't a way to import keymap yet, so it's useless. 2018-06-26 01:53:04 +02:00
Róbert Kiss
873f1de1ef fix: cancelling the key action popover holds its state (#699) 2018-06-25 00:27:14 +02:00
Róbert Kiss
150f993e5f feat: change side menu Agent icon (#698) 2018-06-25 00:05:01 +02:00
Róbert Kiss
06e76e5e0f fix: only flash the remapped key (#697) 2018-06-24 23:39:32 +02:00
Róbert Kiss
a208a264c7 fix: only animate keyboard separator when splitting (#696) 2018-06-24 23:06:33 +02:00
Róbert Kiss
114014fa13 feat: Show not supported OS on firmware page when relevant (#695) 2018-06-24 22:16:00 +02:00
Róbert Kiss
94cfd9d2e9 fix: SwitchKeymapAction not allow to refer to itself (#694) 2018-06-24 20:32:32 +02:00
Róbert Kiss
0aa9c73b4b feat: log firmware version before upgrading firmware (#693) 2018-06-24 19:56:11 +02:00
Róbert Kiss
5234f85dbe fix: close device when any error occurred in the communication (#692) 2018-06-24 18:59:13 +02:00
László Monda
bd8a2f704f Bump version to 1.2.4 and update changelog. 2018-06-21 18:23:49 +02:00
László Monda
439886d69f Replace Linux x86-64 blhost with a statically linked version that should run fine on every Linux distro. 2018-06-21 18:10:55 +02:00
László Monda
b2a37795e3 Add agent-logo-with-text.svg 2018-06-19 23:54:03 +02:00
László Monda
440db56080 Bump Agent version to 1.2.3 and update changelog. 2018-06-19 03:07:26 +02:00
László Monda
337e6e6bb6 Add example to the secondary role tooltip. 2018-06-19 02:44:44 +02:00
László Monda
a1aeda3d35 Further tweak the scancode tooltip. 2018-06-19 01:26:20 +02:00
László Monda
c6a83f8c9b Merge branch 'feat-tooltip-width' 2018-06-19 00:04:14 +02:00
László Monda
0f24427628 Replace the Linux x64 blhost binary with @iprok's version that seems to be more stable. 2018-06-18 23:53:51 +02:00
Róbert Kiss
f52dc36a6a feat: allow wider tooltip width than parent container (#682) 2018-06-18 23:37:15 +02:00
Róbert Kiss
63a936968d feat: allow wider tooltip width than parent container 2018-06-18 23:31:34 +02:00
László Monda
cabfde7963 Tweak the text of the scancode tooltip. 2018-06-18 23:26:06 +02:00
László Monda
79628c2351 Tweak the text of the key remap tooltip. 2018-06-18 22:49:48 +02:00
Róbert Kiss
762fa6f8bf fix: remap SwitchLayerAction 2018-06-18 19:50:34 +02:00
László Monda
a258c097a9 Downgrade to firmware 8.2.5 because it's the latest recommended version that doesn't contain annoying bugs. 2018-06-18 19:14:38 +02:00
László Monda
41faa98fcd Update the firmware update instructions and URL. 2018-06-16 18:22:01 +02:00
Róbert Kiss
c4d7318686 chore: make firmware update log shorter (#675)
* chore: add lodash to the roor package.json

* chore: make firmware update log shorter
2018-06-13 10:07:40 +02:00
Róbert Kiss
9ef11eaa34 feat: add keyboard separator line (#673)
* fix: blhost wrapper return with Promise.reject if error code !== 0

* feat: add keyboard separator line
2018-06-13 00:39:14 +02:00
Róbert Kiss
f34cb2df56 feat: remap key on all keymaps / layers (#668)
* add: popover checkboxs

* feat: add KeyActionRemap

* fix: template driven form checkbox name

* fix: delete key action only if it SwitchLayerAction

* feat: use remap on all keymaps/layers checkbox values in SAVE_KEY action

* feat: set default value to the remapOnAllKeymap and remapOnAllLayer checkbox

* fix: layer mapping
2018-06-10 21:50:49 +02:00
Róbert Kiss
83b9f0d1e9 fix: blhost wrapper return with Promise.reject if error code !== 0 (#669) 2018-06-07 23:38:27 +02:00
Róbert Kiss
7d81cf0c6a style: fix tslint error in switch-layer-action.ts (#666) 2018-06-07 22:54:20 +02:00
tenteen
82b76a9455 Fix left half timeout during fw update (#567) (#626)
The snooze call is skipped in the try block when the command throws an
exception.  As a result, the command tries maxTry times as fast as
possible.

The fix is to move the snooze call outside of the try block.

PS: #GotMyUHK
2018-06-07 22:50:18 +02:00
Róbert Kiss
4ae577f936 feat: make double tap to hold layer optional per key (#662)
* feat: make double tap to hold layer optional per key

* test: fix test serializer

* fix: remove "application start" text

* Add double-tap.svg

* Add closing dot at the end of the sentence.

* fead: add double-tap icon

* Bundle firmware version 8.3.0

* feat: 'layer-double-tap' feature flag

* feat: convert SwitchLayerMode to string enum
2018-06-07 22:11:41 +02:00
László Monda
81a83994ab Make the Export device configuration button show up as a primary button. 2018-05-28 15:58:17 +02:00
László Monda
1d3a3c7f5f Fix changelog. 2018-05-27 20:04:48 +02:00
László Monda
8bb645125d Update changelog and re-release v1.2.2 2018-05-27 02:53:10 +02:00
László Monda
9471b31a5d Instruct the user to power cycle the keyboard when encountered with an invalid hardware configuration. 2018-05-27 02:43:12 +02:00
László Monda
ffa52757c9 Upgrade to firmware 8.2.5 2018-05-27 02:31:22 +02:00
Róbert Kiss
ee53a0df9b feat: set disable style of device name in side menu (#656)
* feat: visualise disabled state of the 'Device name' input control

* fix: original value handling
2018-05-27 02:30:41 +02:00
Róbert Kiss
8e20c85e07 feat: add file upload component (#655)
The component allow upload the same file multiple times
2018-05-26 22:46:41 +02:00
László Monda
65ea786358 Don't include os.platform into the firmware update log because os.type makes it practically redundant. 2018-05-26 17:13:10 +02:00
László Monda
1035837b3b Fix lint error. 2018-05-22 15:29:48 +02:00
Róbert Kiss
18fc2e6b3f fix: permission detection when device not plugged (#653) 2018-05-22 15:11:43 +02:00
László Monda
fc728697d7 Bump version to 1.2.2 and update changelog and package.json 2018-05-22 02:19:48 +02:00
László Monda
cdf3caee9e Display device list at the beginning of the firmware update process. 2018-05-22 01:53:14 +02:00
László Monda
0a4d3a002e Include operating system type to the firmware update log. Add a new tip to the firmware page. 2018-05-22 01:16:02 +02:00
László Monda
d11c532ea4 Add a lot of useful instructions to the firmware page. 2018-05-22 00:38:27 +02:00
László Monda
1ff51697b1 Upgrade from firmware 8.2.2 to 8.2.4 2018-05-21 14:34:42 +02:00
Róbert Kiss
ab8ae31324 fix: permission detection on linux (#651) 2018-05-21 11:46:19 +02:00
László Monda
daa0e723b1 Display more detailed "invalid hardware configuration" errors. 2018-05-21 11:03:05 +02:00
Róbert Kiss
609aba856a fix: permission detection on win and mac (#650) 2018-05-21 11:00:57 +02:00
Róbert Kiss
a6678bd537 feat: update firmware version after update (#649)
* feat: add clipboard copy icon to the x-term-component

* feat: start device poll after firmware upgrade

* feat: remove the OK button from the firmware upgrade page

* feat: read the firmware after firmware upgrade

* fix: scrolling of the x-term-component

* feat: refresh the firmware version after recovery device

* fix: remove the scrollbar styling

* fix: stay on device firmware upgrade screen
2018-05-21 10:57:34 +02:00
László Monda
6c4f580fc2 Check if the keyboard is in factory reset mode and if so, display a relevant instruction. 2018-05-20 01:40:45 +02:00
László Monda
ea41661c65 Add erase-user-config.js 2018-05-19 21:53:41 +02:00
László Monda
c553c7b63b Rename erase-hca.js to erase-hardware-config.js 2018-05-19 21:33:12 +02:00
László Monda
e5988aa800 Rename write-hca.js to write-hardware-config.js 2018-05-19 21:32:15 +02:00
László Monda
ae319c607f Rename write-userconfig.js to write-user-config.js 2018-05-19 21:14:48 +02:00
László Monda
5d23ad1c9e Fix write-hca.js and write-user.js, and remove write-config.js. Fixes #627. 2018-05-19 20:59:11 +02:00
László Monda
55eef50da7 Add erase-hca.js 2018-05-19 20:05:41 +02:00
Róbert Kiss
653465f0e0 feat: device recovery mode (#642)
* add new page and ipc processing

* refactor: remove unused references from uhk.js

* feat: add device recovery route

* refactor: device permission

* feat: write firmware update log to the screen

* fix: xterm height

* feat: add reload button to the recovery page

* refactor: deviceConnectionState.hasPermission in appStartInfo

* refactor: use correct imports

* refactor: move .ok-button css class to the main style.scss

* feat: add bootload active route guard

* style: move RecoveryDeviceAction into new line

* feat: delete reload button

* feat: start device polling after device recovery
2018-05-19 17:22:46 +02:00
László Monda
2cf8044987 Check the signature of the hardware configuration area instead of uniqueId. 2018-05-19 14:07:18 +02:00
László Monda
3c056a7255 Display "Invalid hardware configuration" when the hardware configuration area is uninitialized instead of a general error message. Improves #623. 2018-05-19 13:38:16 +02:00
Róbert Kiss
091796d13c feat: not allow non ascii character in macro action text (#645) 2018-05-17 23:56:36 +02:00
László Monda
eb97dd844f Make the bootloader timeout of the reenumerate script specifiable. 2018-05-16 23:19:36 +02:00
Róbert Kiss
17693ec8fe build: upgrade node.js => 8.11.2 (#641) 2018-05-16 22:08:11 +02:00
Róbert Kiss
7c7ce8f50f feat: only send auto update notification when user indicated the update (#640)
* feat: only send auto update notification when user indicated the update

* fix: add more logging to the auto update process
2018-05-16 22:05:13 +02:00
László Monda
e294727ac5 Bump Agent version to 1.2.1 and update the changelog. 2018-05-12 11:26:41 +02:00
László Monda
f29d64c803 Rename kbootKommandName to kbootCommandName. 2018-05-09 02:16:38 +02:00
László Monda
0385b0ce29 Simply write that the list of available devices is unchanged instead of always listing the devices. 2018-05-09 01:32:55 +02:00
László Monda
b526274cd7 Upgrade to firmware 8.2.2 2018-05-09 00:38:48 +02:00
László Monda
88c16af4a9 Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2018-05-08 17:17:36 +02:00
László Monda
05ac9a6832 Clarify the content of the tooltop regarding non-US characters. 2018-05-08 17:17:04 +02:00
Kristian Sloth Lauszus
04aa5236c2 The usage page and usage number was changed in 6f0b1adc14 (#630) 2018-05-06 21:38:40 +02:00
László Monda
ec98e4e1c6 Rephrase and add explanations about the macro engine not being ready yet. 2018-05-06 03:24:54 +02:00
László Monda
bb9ece494c Update README.md 2018-05-05 21:55:52 +02:00
László Monda
217e6776ac Add changelog entry that I forgot to add. 2018-05-03 01:38:40 +02:00
László Monda
2286218980 Make long media key macro actions work. (#621)
* Make long media key macro actions work.

* fix: macro key action mapping
2018-05-03 00:28:35 +02:00
Róbert Kiss
3d9c83f9f4 build: check node version before build (#625)
* build: check node version before build

* use package.json engine section
2018-05-01 22:50:28 +02:00
Róbert Kiss
14ed163238 chore: npm run clean delete root node_modules and dist folders (#624) 2018-05-01 22:46:23 +02:00
László Monda
c815de0718 Edit phrasing: "... layer by *tapping* this key" 2018-05-01 03:20:44 +02:00
László Monda
6a46556d9e Make get-right-firmware-version.js display not only firmware version but also device protocol version, module protocol version, user config version and hardware config version. 2018-05-01 02:31:17 +02:00
krokofant
f8f820529f Resolve #553 (#614) 2018-04-29 21:32:33 +02:00
László Monda
cac11155e7 Update firmware version to 8.2.0 2018-04-20 10:04:21 +02:00
László Monda
d20870f11e Update firmware version and default mouse speed. 2018-04-20 09:49:27 +02:00
László Monda
10ceb6c79d Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2018-04-20 09:19:58 +02:00
Róbert Kiss
b38b6fa294 build: fix nouislider conflict in webpack conflict (#611)
* build: fix nouislider conflict in webpack conflict and upgrade nouislider

* use package import instead of sub file
2018-04-20 09:19:10 +02:00
László Monda
94c1d35429 Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2018-04-19 22:31:25 +02:00
László Monda
e33cef4e89 Update README. Fix #609 2018-04-13 19:32:20 +02:00
Róbert Kiss
9b815ed9c1 chore: upgrade angular to 5.3.9 and typescript to 2.6.2 (#605)
* chore: upgrade angular to 5.3.9 and typescript to 2.6.2

* fix electron renderer build

* fix webpack config

* format webpack.config

* fix renderer build
2018-04-10 19:52:58 +02:00
László Monda
1d4bb6113c Bump version to 1.1.5 in package.json and update the changelog. 2018-04-10 11:42:34 +02:00
Róbert Kiss
136120b831 feat: not allow run multiple Agent (#603) 2018-04-09 21:09:09 +02:00
Róbert Kiss
cd299c06d6 feat: not allow run multiple Agent (#604) 2018-04-09 20:48:22 +02:00
László Monda
e152a36ad7 Add agent-app-icon.png which I forgot to add. 2018-04-09 14:16:09 +02:00
László Monda
e90544db33 Bump Agent version to 1.1.4 in package.json and update the changelog. 2018-04-09 13:59:38 +02:00
László Monda
7ceca202b4 Reposition the ISO key in the scancode list. 2018-04-09 12:48:44 +02:00
László Monda
ddc65aa54b Replace application icon with a diagonal gradient based icon that should look better on the desktop. 2018-04-09 12:20:46 +02:00
Róbert Kiss
13ec617d58 Make saving the configuration more robust (#594)
* feat: Make saving the configuration more robust

* parse backup user config before return

* fix some bug

* Add write-userconfig.js and invalid-config.bin

* throw exception if failed user config parsing

* Merge branch 'master' into feat-467-make-save-more-robust

* hide keymaps and macros if agent in restore mode

* fix Device name settings
2018-04-09 10:11:26 +02:00
Róbert Kiss
00c5b69129 fix: display agent icon when user use ALT + TAB (#600) 2018-04-07 23:17:50 +02:00
Róbert Kiss
6ccf005750 feat: Handle privilege escalation gracefully even without PolicyKit (#599)
* feat: Handle privilege escalation gracefully even without PolicyKit

* build: upgrade tslint => 5.9.1

* build: add uhk-agent/package-lock.json

* feat: add error animation

* fix: display agent icon when user use ALT + TAB
2018-04-07 23:09:47 +02:00
László Monda
6e1f0ded9e Bump Agent version to 1.1.3 and update changelog. 2018-04-06 16:26:21 +02:00
László Monda
d58386ef4b Reference firmware 8.1.5 2018-04-04 15:51:04 +02:00
László Monda
179c982bfb Make the Fn+Backspace shortcut of the QWERTY for PC keymap switch to the TES keymap for testing purposes. 2018-04-03 23:25:30 +02:00
László Monda
a7d07dbf4c Make factory-update.js allow the layout to be set. 2018-04-03 21:04:47 +02:00
László Monda
0ca922d24a Sleep 1s between sending reset and idle kboot commands. Hopefully this will always make the left half resume after firmware updates. 2018-04-03 00:49:51 +02:00
László Monda
a6f1aa15a5 Supply the correct configBufferId values to launchEepromTransfer() and get rid of the obsoleted eepromTransfer mapping. 2018-04-02 23:54:48 +02:00
László Monda
fc2d025cc4 Don't display the buffer related USB transfers of writeConfig() because they generate too much noise. 2018-04-02 23:32:13 +02:00
László Monda
8b5ae106bd Display kboot command names instead of numberic ids. 2018-04-02 23:20:07 +02:00
László Monda
44639bbf53 Make apply-config.js and get-module-state.js use read() instead of readSync() 2018-04-02 22:56:28 +02:00
László Monda
9fcce9234a Make getDeviceState await. Dump transfer descriptions before the actual transfers when the debug mode is on. 2018-04-02 21:35:47 +02:00
László Monda
b26fecfc7a Make factory-update.js switch keymap. 2018-04-02 18:28:12 +02:00
László Monda
1b15911783 Extract code to uhk.writeUca() 2018-04-02 17:31:27 +02:00
László Monda
148dd8d361 Rewrite writeHca() using the JavaScript USB API instead of TypeScript because the latter couldn't reopen the USB device. 2018-04-02 17:25:35 +02:00
László Monda
0d9ac50999 Move writeHca() to uhk.js 2018-04-02 14:47:48 +02:00
László Monda
e19e4bc5a4 Move the gist of write-hca.js into writeHca() 2018-04-02 14:43:11 +02:00
László Monda
bdd79a5a9a Clean up writeConfig() a bit. 2018-04-02 14:32:45 +02:00
László Monda
fb4e05fdc4 Dump USB reads and writes via writeDevice() 2018-04-02 00:30:03 +02:00
László Monda
01fcf9053a Strip down factory-update.js to its essentials. 2018-04-02 00:11:35 +02:00
László Monda
533c2f13d2 Copy update-firmwares.js as factory-update.js 2018-04-01 23:58:13 +02:00
László Monda
b9c32b46a9 Extract uhk.applyConfig() and uhk.launchEepromTransfer() 2018-04-01 23:55:40 +02:00
László Monda
f9b7260be6 Extract uhk.writeUserConfig() 2018-04-01 01:23:50 +02:00
László Monda
847694d590 Rewrite write-config.js using async/await. 2018-04-01 01:13:17 +02:00
László Monda
58178a5c7b Extract uhk.updateFirmwares() 2018-03-31 23:50:02 +02:00
László Monda
9b93b4dac5 Rename update-all-firmwares.js to update-firmwares.js 2018-03-31 23:24:34 +02:00
Róbert Kiss
478dac0621 build: extract electron dependencies to the root package.json (#593) 2018-03-31 22:11:41 +02:00
László Monda
f196fcdaa2 Make switch-keymap.js accept a keymap abbreviation. 2018-03-30 19:17:42 +02:00
László Monda
0b420ff516 Extract uhk.switchKeymap() 2018-03-30 18:59:50 +02:00
László Monda
7656af76e4 Add switch-keymap.js 2018-03-30 12:48:28 +02:00
László Monda
beed546ae4 Remove switch keymap actions that point to the TES keymap. 2018-03-29 19:09:00 +02:00
László Monda
bf94370f2f Add the "Export device configuration" button instead of the export link. Make the button export JSON by default and BIN when pressed with Shift. 2018-03-29 18:44:09 +02:00
László Monda
2476049681 Out of play, play/pause, stop, and pause only leave play/pause in the default configuration. 2018-03-29 18:12:08 +02:00
László Monda
f8d8b6d213 Remove Launch Browser, Launch Calculator, and Eject Disk key actions from the default configurations because they don't work reliably across OSes and very rarely used. 2018-03-29 14:51:06 +02:00
László Monda
05bbce1d50 Remove redundant stop media playback actions. 2018-03-29 13:27:13 +02:00
László Monda
32494fa228 Remove relative switch keymap actions. 2018-03-29 13:12:27 +02:00
László Monda
e0ce38988e Remove shut down key actions, only keep sleep key actions, and bind them to the \ key. 2018-03-29 12:54:51 +02:00
László Monda
5c660c549d Update the test keymap. This one should be the final version. 2018-03-28 20:56:56 +02:00
László Monda
510b914e26 Change terminology from download / upload to export / import for greater clarity. 2018-03-28 18:27:27 +02:00
László Monda
cf64fc0c08 Make the tooltip text regarding non-US characters easier to understand. 2018-03-25 22:24:33 +02:00
Róbert Kiss
b25bc9d81d feat: show firmware version of the device/modules on firmware page (#589) 2018-03-23 07:10:02 +01:00
Róbert Kiss
2f00a5eaf4 feat: enhance device firmware page (#588)
* feat: enhance device firmware page

* remove confirmation dialog from firmware upgrade buttons
2018-03-15 12:20:35 +01:00
László Monda
e8fe0f8d3e Fix menu scancode. (#586)
* Fix menu scancode.

* Change the old menu key scancode 118 to 101.

* validate scancodes
2018-03-11 22:56:12 +01:00
László Monda
e84dbf2c15 Bump Agent version to 1.1.2. Bump firmware version to 8.1.4. Update changelog. 2018-03-09 19:02:46 +01:00
Róbert Kiss
990ff8e980 ci: mac code sign (#585)
* ci: register certificate into the mac keychain

* try to not use yarn electron-builder installer on mac

* use -P

* debug electron-osx-sign

* use cscLink

* use xcode xcode9.3beta

* increase package.json version

* revert version to 1.1.1

* delete unused files

* format release.js file

* format release.js file

* format release.js file
2018-03-09 18:31:48 +01:00
Róbert Kiss
1ca8e67e52 try to use xcode 9.2 (#584) 2018-03-08 18:09:17 +01:00
Róbert Kiss
23cb583bf7 chore: upgrade lerna => 2.9.0 (#583) 2018-03-06 00:34:06 +01:00
Róbert Kiss
d5cc735b85 build: Windows code sign (#581)
* build: Add windows certificate

* temporary bump version

* change CSC_LINK settings

* rdp connection

* remove appveyor RDP block
2018-03-01 17:29:55 +01:00
László Monda
1981311136 Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2018-02-28 00:31:48 +01:00
László Monda
bbb5d4a35b Add a notice regarding the lack of robustness of the firmware update process. 2018-02-28 00:31:06 +01:00
serge-rosov
58ef40fb02 year correction (#582) 2018-02-27 18:11:46 +01:00
Róbert Kiss
b8f35df155 build: switch off mac codesign 2018-02-24 23:33:22 +01:00
Róbert Kiss
c3e712851c build: Sign mac installer 2018-02-24 23:12:01 +01:00
László Monda
2eaa1e0634 Update package-lock.json 2018-02-19 17:11:16 +01:00
László Monda
6ee21bcd7a Fix get-debug-info.js 2018-02-18 03:44:17 +01:00
Róbert Kiss
10ae68ad4b fix(keymap): Fix undefined keymap serialization (#573) 2018-02-17 18:07:33 +01:00
László Monda
02044ae1d0 Bump version to 1.1.1 and update changelog. 2018-02-13 03:54:28 +01:00
László Monda
3f99d47bba Use firmware 8.1.2 2018-02-13 03:40:52 +01:00
László Monda
9beadb4aac Add keymap descriptions. 2018-02-13 03:24:06 +01:00
László Monda
d9fb7a4b42 Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2018-02-13 03:05:18 +01:00
László Monda
83912ec21f Assign "switch to test keymap" action on all keymaps. 2018-02-13 03:04:36 +01:00
Róbert Kiss
6c7232a5ba feat(device): Make Agent able to unbrick bricked modules (#577) 2018-02-13 02:11:27 +01:00
László Monda
65fc8b5efb Tweak the scancode related help text. 2018-02-12 15:36:39 +01:00
Róbert Kiss
7a64191955 fix(config): Save reset user configuration in web module (#574) 2018-02-11 20:44:08 +01:00
Róbert Kiss
1a413c824e Fix 560 delete bind play macro action when macro delete (#576)
* fix(config): delete KeyAction binding of deleted macro

* refactor: use sorter import

* fix(macro): read the macro id from route params

* fix(keyAction): use NoneAction in keyAction mapping
2018-02-11 20:12:12 +01:00
Róbert Kiss
e545c9d67b feat(popover): Add Non-US help tooltip (#578) 2018-02-11 19:47:41 +01:00
László Monda
8650fef7ae Make reenumerate() more reliable. 2018-01-31 04:26:49 +01:00
László Monda
5c618869a2 Use updateDeviceFirmware(), reenumerate(), and updateModuleFirmware() in update-all-firmwares.js instead of forking processes. 2018-01-31 03:40:46 +01:00
László Monda
1b8d6949e0 Extract updateModuleFirmware() to uhk.js 2018-01-31 03:03:34 +01:00
László Monda
aabc0a8746 Use waitForKbootIdle() in update-module-firmware.js 2018-01-31 02:28:29 +01:00
László Monda
9589398834 Move waitForKbootIdle() to uhk.js 2018-01-31 02:25:37 +01:00
László Monda
933a715ea5 Extract waitForKbootIdle() 2018-01-31 02:22:41 +01:00
László Monda
df14e2d569 Extract uhk.jumpToModuleBootloader() and use it. 2018-01-31 01:17:28 +01:00
László Monda
4f8a0247d3 Call uhk.sendKbootCommandToModule() instead of forking send-kboot-command-to-module.js 2018-01-31 00:57:53 +01:00
László Monda
85ec5f6b6a Make update-module-firmware.js use reenumerate() and sendKbootCommandToModule() instead of forking more processes. 2018-01-31 00:17:27 +01:00
László Monda
739b830f47 Use uhk.writeDevice() in jump-to-module-bootloader.js 2018-01-31 00:09:16 +01:00
László Monda
482cff3d3b Extract sendKbootCommandToModule() from send-kboot-command-to-module.js to uhk.js 2018-01-30 23:44:25 +01:00
László Monda
9284ae5032 Use reenumerate() instead of forking reenumerate.js 2018-01-30 23:32:37 +01:00
László Monda
cac6fdc190 Move updateDeviceFirmware() from update-device-firmware.js to uhk.js 2018-01-30 21:48:42 +01:00
László Monda
ca9bf60a1b Extract updateDeviceFirmware() 2018-01-30 21:46:39 +01:00
László Monda
bb7edb8e4d Remove shared.js 2018-01-30 21:39:05 +01:00
László Monda
0d9c976eb8 Move execRetry() from shared.js to uhk.js 2018-01-30 21:37:27 +01:00
László Monda
288d4f75b6 Move getBlhostCmd() from shared.js to uhk.js 2018-01-30 21:35:35 +01:00
László Monda
73e07eae2d Move checkFirmwareImage() from shared.js to uhk.js 2018-01-30 21:28:00 +01:00
László Monda
8e620caac5 Use reenumerate() in update-device-firmware.js instead of forking reenumerate.js 2018-01-30 20:39:48 +01:00
László Monda
0d4e1acf76 Extract reenumerate() from reenumerate.js to uhk.js 2018-01-30 19:34:29 +01:00
László Monda
88c42d58b1 Extract reenumerate() as an async function in reenumerate.js 2018-01-30 18:36:16 +01:00
László Monda
5099e904fc Use uhk.writeDevice() in set-i2c-baud-rate.js 2018-01-30 17:30:12 +01:00
László Monda
5476f7c3a5 Extract I2C0_F. 2018-01-30 17:25:29 +01:00
László Monda
e0bb0bcca3 Add the async uhk.writeDevice() and use it in get-i2c-baud-rate.js 2018-01-30 17:21:50 +01:00
László Monda
124c3ec29b Simplify reenumerate.js mainly by using uint32ToArray() 2018-01-30 06:09:09 +01:00
László Monda
2310320b8a Replace pushUint32() with uint32ToArray() and don't use it where not needed. 2018-01-30 05:30:32 +01:00
László Monda
67346b4cda Simplify getTransferData() 2018-01-30 05:10:29 +01:00
Róbert Kiss
662ca0152f fix(keymap): Don't show keymap description in the key action popover (#572) 2018-01-29 23:40:41 +01:00
Róbert Kiss
6358528438 ci: use node 8.9.4 (#571)
Node 8.9.4 contain a few security update and use npm 5.6.0 so not need to
install npm in the build process. The build time will a few sec sorter
2018-01-29 23:20:07 +01:00
József Farkas
02f1053d46 feat(popover): sort keymaps and macros alphabetically (#534)
* feat(popover): sort keymaps and macros alphabetically

Closes #512

* small performance refactoring
2018-01-29 23:15:21 +01:00
Róbert Kiss
a44a7dc5f8 chore: use lodash-es version (#569)
lodash-es support tree shaking and the bundle size will near 2MB less
2018-01-29 23:02:17 +01:00
Róbert Kiss
02d57fdabf feat(keymap): add description to keymap (#559)
* feat(keymap): add description to keymaps

* add new feature request

* preserve new lines
2018-01-29 22:54:29 +01:00
Róbert Kiss
38f6688930 chore: upgrade electron => 1.7.11 (#568) 2018-01-29 21:26:32 +01:00
Róbert Kiss
6ca12d0ccd build: set Apple appId => com.ultimategadgetlabs.agent (#566) 2018-01-23 19:19:15 +01:00
László Monda
acd17ac657 Fix the background color of the toplevel device node. Fixes #552 2018-01-22 04:41:43 +01:00
László Monda
5393501f68 Update CHANGELOG.md 2018-01-16 19:43:10 +01:00
László Monda
99e020d66f Bump Agent version to 1.1.0 and update package.json and the changelog accordingly. 2018-01-15 19:02:29 +01:00
László Monda
2c74ce8d3e Reference firmware 8.1.0 and adopt the newly introduced "v" git tag prefix for firmware tags. 2018-01-15 12:04:11 +01:00
László Monda
3cd2d208b9 Make get-i2c-health.js output uptime and baud rate. 2018-01-15 01:37:52 +01:00
László Monda
d0cd30f915 Add get-i2c-health.js 2018-01-15 01:21:56 +01:00
László Monda
010a23aaeb Extract slaveI2cErrorBufferToString() 2018-01-15 01:07:23 +01:00
László Monda
c723fe2651 Simplify get-slave-i2c-errors.js by using padEnd() 2018-01-15 00:55:38 +01:00
László Monda
95caa58624 Utilize uhk.getUint* in get-slave-i2c-errors.js 2018-01-15 00:37:31 +01:00
László Monda
9089f088b6 Clean up get-left-firmware-version.js a bit. 2018-01-15 00:31:24 +01:00
László Monda
1aeb4e8326 Make get-left-firmware-version.js display module protocol version, too. 2018-01-15 00:07:30 +01:00
László Monda
96b9226adb Fix script to display the correct firmware version. 2018-01-15 00:02:57 +01:00
László Monda
7c065f4368 Remove --buspal speed specification because it gets disrespected by the firmware anyways. 2018-01-14 22:13:31 +01:00
Róbert Kiss
a8108b9abf revert: Revert auto write user configuration (#558) 2018-01-14 19:32:41 +01:00
László Monda
c7baa00720 Add get-uptime.js 2018-01-14 18:33:14 +01:00
László Monda
5cdf2282f8 Add {get,set}-i2c-baud-rate.js 2018-01-14 18:15:50 +01:00
László Monda
89221faf60 Add set-i2c-baud-rate.js 2018-01-14 00:45:51 +01:00
László Monda
3b70c84c61 Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2018-01-13 19:33:28 +01:00
László Monda
5b1f4cb584 On the mouse speed section of the key action popover remove the bottom sentence and slightly rephrase the top sentence. 2018-01-13 19:30:08 +01:00
Mikko Lakomaa
3ee6c680a1 Agent menu (#540)
* Add generate version module script

* Remove Fork me on GitHub banner

* Add app-version.ts

* Revert "Add app-version.ts"

This reverts commit fe1a37e631.

* Add app-version.ts

* Add agent icon class

* Move settings component under agent folder

* Add AboutComponent

* Add agent routes

* Add index.ts for agent folder

* Fix agent folder imports in shared module

* Add agent menu to side menu, with Settings and About pages under it

* Fix agent icon alignment in side menu

* Simplify About page

* Make Agent menu 0 level in side menu

* Remove bottom Settings menu

* Fix Agent menu closing if My UHK is closed in side menu

* Fix version text alignment in auto update settings

* Remove github fork ribbon styles

* use package.json instead of app-version.ts

* fix OpenUrlInNewWindow naming

* fix lint request

* fix: firmware download url calculation
2018-01-13 17:10:21 +01:00
László Monda
fdcf64d5c6 Only display minutes in the I2C error logger script. 2018-01-13 00:06:18 +01:00
László Monda
6c327ee414 Add I2C logger script. 2018-01-11 02:57:49 +01:00
László Monda
b6bdd1486c Make update-module-firmware.js more robust and able to recover bricked modules (including the left half) by utilizing the newly added wait-for-kboot-idle.js 2018-01-10 03:15:19 +01:00
László Monda
bd5be98d99 Restore wdi-simple.exe and 50-uhk60-rules.cmd just in case. 2018-01-08 06:13:37 +01:00
László Monda
802e6a4649 Update README.md 2018-01-08 06:03:58 +01:00
László Monda
ae11c01725 Tidy up else clauses based on our coding standards. 2018-01-08 05:41:36 +01:00
Róbert Kiss
f0139c55ee feat(user-config): Upload user configuration from json/bin file (#545)
* feat(user-config): Upload user configuration from json/bin file

* fix error message

* remove file extension filter

* apply user config after loaded from file

* add file filter

* remove file filter
2018-01-08 05:29:31 +01:00
László Monda
b3f2e3451e Update README.md 2018-01-07 21:30:03 +01:00
Róbert Kiss
906beaac0e Update README.md (#548) 2018-01-07 21:27:07 +01:00
Andrew Kraut
46f855d1db Update readme (#547)
* Fix typos

* Update build instructions
2018-01-07 19:56:24 +01:00
László Monda
5341d953ff Fix statusCodesToStrings map. 2018-01-07 05:21:08 +01:00
László Monda
bd9a2a0eeb Make get-slave-i2c-errors.js display slave names and I2C error names. 2018-01-06 21:29:14 +01:00
László Monda
4c10954721 Add script which reads I2C errors. 2018-01-05 03:26:26 +01:00
Róbert Kiss
bbce1e0e0f fix(user-config): Validate device, keymap, and macro names (#543)
* fix(user-config): Validate device, keymap, and macro names

* fix device name renaming
2018-01-03 21:06:08 +01:00
László Monda
13f064229f Add keyAssigmentOrder array to uhk60-right/device.json 2018-01-02 04:05:26 +01:00
466 changed files with 39416 additions and 31362 deletions

2
.nvmrc
View File

@@ -1 +1 @@
8.9.1
10.11.0

View File

@@ -11,7 +11,7 @@ matrix:
fast_finish: true
include:
- os: osx
osx_image: xcode8.3
osx_image: xcode9.3beta
- os: linux
env: CC=clang CXX=clang++ npm_config_clang=1
compiler: clang
@@ -45,7 +45,6 @@ addons:
install:
- nvm install
- npm i -g npm@5.6.0
- npm install
before_script:

View File

@@ -4,15 +4,248 @@ All notable changes to this project will be documented in this file.
The format is loosely based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
Every Agent version includes the most recent firmware version. See the [firmware changelog](https://github.com/UltimateHackingKeyboard/firmware/blob/master/CHANGELOG.md).
## [1.2.13] - 2019-07-14
Firmware: 8.5.**4** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.5.4)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
- Implement the Kinetis bootloader protocol natively instead of relying on blhost.
- Fix device recovery mode.
- Correctly display whether the UHK is detected.
- Don't disable input in the key action popover after adding a layer switch action, deleting it, and trying to edit it on its layer.
- Don't change tab immediately upon closing the key action popover.
- Fix UI glitch that occurrs when hitting Tab after updating keymap description.
- Make the Agent icon slightly smaller to be consistent with most application icons.
- Redesign the About page.
- Link the UHK knowledgebase from the help page.
- Make the middle mouse button not open new windows on links in Agent.
- Add top auto-update notification bar.
- Save window state when closing Agent.
- Hide USB usage data in console.
## [1.2.12] - 2018-11-14
Firmware: 8.**5.3** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.5.3)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
- When the firmware of the right keyboard half is larger or equal than 8.4.3 then display the "Lock layer when double tapping this key" checkbox and remove "... macro playback is not implemented yet..." notices.
- Upgrade to node-hid 0.7.3 which utilizes the hidraw USB driver on Linux instead of libusb.
- Update udev rules for the new hidraw based node-hid.
- Improve the "Cannot find your UHK" and the privilege escalation screens to show more relevant messages when transitioning from the libusb based node-hid to the hidraw based node-hid.
- Fix the rendering of macro actions, so that their text doesn't overlap.
- Add "International {1,2,3}" and "Language {1,2}" keypress actions.
- Add icon for the Play/Pause keypress action.
- Remove the Stop/Eject keypress action.
- Make the "Type text" macro action accept clipboard data on Mac.
- Display "You can't change this mapping because on the base layer a layer switcher key targets this key." in the key action popover whenever it applies.
- Fix UI bug which could be triggered by tapping Tab in the keymap abbreviation input.
- Don't trigger Agent shortcuts when capturing keypresses.
- Log USB device list before checking permissions.
- Show OS-specific modifiers in the title bar of macro actions.
- Only show the device list on Linux when the list actually changes.
## [1.2.11] - 2018-10-03
Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
- Add backspace and caps lock icons which avoids the overlap of their old texts.
- Fix right and middle mouse click macro actions which were exchanged.
- Include Agent version to the firmware update log.
## [1.2.10] - 2018-09-24
Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
- Add History Back and History Forward scancodes.
- Save the actual decelerated scroll speed instead of using the accelerated scroll speed by accident.
- Allow layer switcher secondary roles only on the base layer.
- When remapping modifiers, display a warning suggesting to remap them on all layers.
- Display more exact instructions on the permission setup screen.
- Set the decelerated scroll speed of the default configuration from 20 to 10.
- Map Caps Lock without Ctrl on default keymaps.
- Rename "Scroll Lock" to "ScrLk" and "Num Lock" to "NumLk" on keys to avoid text overlap.
- In the scancode select2, display "Print Screen SysRq" and add SysRq above PrtScn when rendering the key.
- Fix left and right direction titles for mouse movement macro actions.
## [1.2.9] - 2018-09-13
Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
- Display OS-specific modifiers.
- Display secondary roles.
- Don't trigger "Remap on all layers" after leaving Agent with Alt+Tab.
## [1.2.8] - 2018-08-26
Firmware: 8.**2.5** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
- Uncheck the "Remap on all keymaps" and "Remap on all layers" checkboxes of the key action popover by default.
- Bind left and right Shift on the Mouse layer of all keymaps in the default configuration.
- Make ng2-select2 widgets faster.
- Add note to the LED brightness page saying that current UHK versions are not backlit.
- Fix the padding of the secondary role tooltip.
- Remove the redundant scrollbar from the LED brightness page.
## [1.2.7] - 2018-07-26
Firmware: 8.4.0 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.4.0)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
- Fix Agent startup exception on Linux by upgrading Electron builder.
- Change the shortcut which enables the USB stack test code, so that it can be triggered with the default Mac US keymap.
## [1.2.6] - 2018-07-26
Firmware: 8.**4.0** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.4.0)] | Device Protocol: 4.**4.0** | User Config: 4.0.1 | Hardware Config: 1.0.0
- Replace the Linux blhost binary with a statically compiled version that doesn't use special instructions and shouldn't segfault.
- Keep the current layer when changing keymaps.
- Fix the sleep key of Mac keymaps.
- Add help page.
- Add "save to keyboard" and "remap key" shortcuts.
- Build only AppImages for Linux.
- Replace ng2-select2 widgets with ngx-select-ex that always shows up in the correct position.
- Improve the phrasing of the firmware update error message.
- Tweak unsupported Windows firmware update notification.
- Hide the Settings menu until auto update is implemented.
- Don't scroll when the macro tab of the key action popover gets selected.
- Add keyboard shortcut for enabling the USB stack test mode of the firmware. `DEVICEPROTOCOL:MINOR`
- Tone down the color of the separator line.
## [1.2.5] - 2018-06-26
Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.3.1 | User Config: 4.0.1 | Hardware Config: 1.0.0
- When remapping a switch keymap action on all keymaps, don't set it on its own keymap.
- Make the key action popover always contain the action of the current key, even after cancelled.
- Include the firmware version to be updated to the firmware update log.
- Update the Agent icon of the side menu and the about page.
- When remapping a key, only flash the affected key instead of all keys.
- Fade in/out the keyboard separator line only when splitting the keyboard.
- Only show the unsupported OS message of the firmware page on relevant Windows versions.
- Close and reopen USB device when an error occurs.
- Temporarily remove the export keymap feature because it's useless until import is implemented.
## [1.2.4] - 2018-06-21
Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.3.1 | User Config: 4.0.1 | Hardware Config: 1.0.0
- Replace Linux x86-64 blhost with a statically linked version which should make firmware updates work on every Linux distro.
## [1.2.3] - 2018-06-19
Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.3.1 | User Config: 4.0.1 | Hardware Config: 1.0.0
- Add checkboxes for remapping keys on all layers and/or all keymaps.
- Add separator line between the keyboard halves.
- Add double tap icon for switch layer actions.
- Improve the looks and content of the tooltips of the key action popover.
- Make the left keyboard half less likely to timeout during firmware update.
- Terminate the firmware update process if blhost segfaults.
- Replace the Linux x86-64 version of the blhost binary which should not make it segfault anymore.
- Make the firmware update log shorter by listing one device per line and not repeating the list of available USB devices.
- Make the firmware update help text shorter.
## [1.2.2] - 2018-05-27
Firmware: 8.2.**5** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.3.**1** | User Config: 4.0.1 | Hardware Config: 1.0.0
- Offer recovery for bricked right keyboard halfs.
- Detect when the hardware configuration of a device is invalid and display a notification. `DEVICEPROTOCOL:PATCH`
- Check if the keyboard is in factory reset mode and if so, display a relevant instruction.
- Only allow ASCII characters in type text macro actions.
- Allow uploading the same file multiple times in a row.
- Only send auto update notification when the user initiates the update.
- Update the firmware versions on the firmware update page right after firmware updates.
- Add a lot of useful instructions to the firmware page to help users update the firmware.
- Add the operating system and initial device list to the firmware update log.
- Add copy to clipboard button to the top right corner of the firmware update terminal widget.
## [1.2.1] - 2018-05-12
Firmware: 8.2.**2** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.2)] | Device Protocol: 4.3.0 | User Config: 4.0.**1** | Hardware Config: 1.0.0
- Match for the new USB usage page and usage number. This is critical for UHKs flashed with firmware >=8.2.2 to be recognized by Agent on OSX.
- Make the config serializer handle long media macro actions. `USERCONFIG:PATCH`
- Add note on the macro page explaining that the macro engine of the firmware is not ready yet.
- Add an example to the scancode tooltip to better explain users how to invoke non-US characters.
## [1.2.0] - 2018-04-20
Firmware: 8.**2.0** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.0)] | Device Protocol: 4.**3.0** | User Config: 4.0.0 | Hardware Config: 1.0.0
- Tweak the default mouse speed. This was necessary because the last firmware version adjusted speed multipliers. The mouse speed can be reset via the "Reset speeds to default" button of the "Mouse speed" page.
- Make the newly added switch-keymap.js script utilize the new UsbCommandId_SwitchKeymap, allowing for programmatic keymap switching. `DEVICEPROTOCOL:MINOR`
## [1.1.5] - 2018-04-10
Firmware: 8.1.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.5)] | Device Protocol: 4.2.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
- Don't allow to run multiple instances of Agent at the same time, but rather focus the already existing Agent window.
## [1.1.4] - 2018-04-09
Firmware: 8.1.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.5)] | Device Protocol: 4.2.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
- Handle privilege escalation gracefully on Linux even without PolicyKit.
- Fix application icon path.
- Replace application icon with a diagonal gradient based icon that should look better on desktop.
- Make saving the configuration more robust, and add a configuration recovery screen.
- Reposition the ISO key in the scancode list.
## [1.1.3] - 2018-04-06
Firmware: 8.1.**5** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.5)] | Device Protocol: 4.2.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
- Show the firmware versions of the left and right keyboard halves on the firmware page.
- Fix menu scancode.
- Make the tooltip text regarding non-US characters easier to understand.
- On the Device Configuration page change terminology from download/upload to export/import for greater clarity.
## [1.1.2] - 2018-03-09
Firmware: 8.1.**4** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.4)] | Device Protocol: 4.2.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
- Fix the configuration serializer so that the correct key actions get serialized, and the save button always appears when needed.
- Add instructions to the firmware page to aid users.
- Fix code signing on OSX.
- Sign Agent on Windows.
## [1.1.1] - 2018-02-13
Firmware: 8.1.**2** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.2)] | Device Protocol: 4.2.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
- Sign Agent on OSX resulting in easier installation.
- Add per-keymap description field.
- Sort keymaps and macros alphabetically within the key action popover.
- Add tooltip regarding non-US scancodes.
- When deleting a macro, also delete the relevant play macro actions.
- Make the reset configuration button persist the reset configuration in Agent-web.
- Make Agent able to unbrick bricked modules.
- Assign "switch to test keymap" action on all keymaps in the default configuration.
- Add keymap descriptions in the default configuration.
## [1.1.0] - 2018-01-15
Firmware: 8.**1**.0 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.0)] | Device Protocol: 4.**2.0** | User Config: 4.0.0 | Hardware Config: 1.0.0
- Only accept device, keymap, and macro names upon editing if their trimmed length is non-zero.
- Add diagnostics USB scripts, most notably /packages/usb/{get-i2c-health,set-i2c-baud-rate}.js, some utilizing new device protocol commands and properties. `DEVICEPROTOCOL:MINOR`
- Implement the Device -> Upload device configuration feature.
- Make update-module-firmware.js more robust and able to recover bricked modules (including the left half) by utilizing the newly added wait-for-kboot-idle.js. `DEVICEPROTOCOL:MINOR`
- Add the Agent -> About page containing the version number of Agent.
- On the mouse speed section of the key action popover, remove the now incorrect bottom sentence and slightly rephrase the top sentence.
- Remove --buspal speed specification argument because it gets disrespected by the firmware anyways.
- Fix get-left-firmware-version.js to display the correct firmware version.
## [1.0.4] - 2017-12-30
Firmware: [8.0.0](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/8.0.0) | Device Protocol: 4.0.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
Firmware: 8.0.0 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.0.0)] | Device Protocol: 4.0.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
- Add mouse speed settings.
## [1.0.3] - 2017-12-28
Firmware: [8.0.**0**](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/8.0.**0**) | Device Protocol: 4.0.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
Firmware: 8.0.**0** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.0.0)] | Device Protocol: 4.0.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
- Add LED brightness settings.
- Some key actions, for example Left Arrow were displayed as text with modifiers and as icon without modifires. Now, they're always displayed as icons.
@@ -23,18 +256,18 @@ Firmware: [8.0.**0**](https://github.com/UltimateHackingKeyboard/firmware/releas
## [1.0.2] - 2017-12-25
Firmware: [**8.0.1**](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/8.0.1) | Device Protocol: 4.0.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
Firmware: **8.0.1**[[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.0.1)] | Device Protocol: 4.0.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
- Fix firmware upgrade on Linux.
## [1.0.1] - 2017-12-22
Firmware: [7.0.0](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/7.0.0) | Device Protocol: 4.0.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
Firmware: 7.0.0[[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v7.0.0)] | Device Protocol: 4.0.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
- Fix Linux privilege escalation when udev rules aren't set up.
## [1.0.0] - 2017-12-14
Firmware: [**7**.0.0](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/7.0.0) | Device Protocol: 4.0.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
Firmware: **7**.0.0[[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v7.0.0)] | Device Protocol: 4.0.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
- First release

View File

@@ -1,3 +1,17 @@
# Signing the CLA
Before contributing to this project, you must sign [the CLA](/cla/cla-1.0.0.md).
To sign the CLA, add your GitHub username to the end of the CLA. Make sure that the usernames remain alphabetically sorted.
Then create a pull request with the title:
> Sign CLA
and with the body:
> I have read the Agreement, and fully agree to it by signing it with my GitHub username.
# Bug reports
If the build process fails, please open a [new issue](https://github.com/UltimateHackingKeyboard/agent/issues/new) containing the complete build log.

8
ISSUE_TEMPLATE Normal file
View File

@@ -0,0 +1,8 @@
Before submitting a new issue, make sure to do the following:
1. If you're using Karabiner Elements on your Mac, close it!
2. Install the latest Agent:
https://github.com/UltimateHackingKeyboard/agent/releases/latest
3. Use Agent to update to the latest firmware:
https://github.com/UltimateHackingKeyboard/firmware/releases/latest
4. Try to reproduce the issue, and only report it if it still persists.

View File

@@ -5,30 +5,27 @@
Agent is the configuration application of the [Ultimate Hacking Keyboard](https://ultimatehackingkeyboard.com/).
[Give it a whirl!](http://ultimatehackingkeyboard.github.io/agent/)
## Two builds to rule them all
It's worth mentioning that Agent has two builds.
The **electron build** is the desktop application which is meant to be used if you have an actual UHK at hand. It starts with an opening screen which detects your UHK. You cannot get past this screen without connecting a UHK via USB.
The **web build** is meant to be used for demonstation purposes, so people who don't yet own a UHK can get a feel of Agent and its capabilities in their browser. Eventually, WebUSB support will be added to the web build, making it able to communicate with the UHK. Given the sandboxed nature of browers, the web build will always lack features that the electron build offers, so this won't make the electron build obsolete.
The two builds share code as much as possible.
* Try out the [web build of Agent](http://ultimatehackingkeyboard.github.io/agent/) in your browser. This is meant to be used for demonstration purposes.
* Download the [desktop build of Agent](https://github.com/UltimateHackingKeyboard/agent/releases) from our releases page. Use this if you have an actual UHK at hand, or else you won't get past the opening screen!
## Building the electron application
First up, make sure that node >=8.1.x and npm >=5.1.x are installed on your system. Next up:
### Step 1: Build Dependencies
You'll need Node.js LTS. Use your OS package manager to install it. [Check the NodeJS site for more info.](https://nodejs.org/en/download/package-manager/ "Installing Node.js via package manager") Mac OS users can simply `brew install node` to get both. Should you need multiple Node.js versions on the same computer, use Node Version Manager for [Mac/Linux](https://github.com/creationix/nvm) or for [Windows](https://github.com/coreybutler/nvm-windows)
You'll also need `libusb`.
On debian-based linux distros, `apt-get install libusb-dev libudev-dev g++` is sufficient.
On Mac OS, use `brew install libusb libusb-compat`.
For everyone else, use the appropriate package manager for your OS.
### Step 2: Build Environment
```
# Execute the following line on Linux. Use relevant package manager and package names on non-Debian based distros.
apt-get install libusb-dev libudev-dev g++
git clone git@github.com:UltimateHackingKeyboard/agent.git
cd agent
npm install
npm run build:electron
npm run build
npm run electron
```

View File

@@ -1,10 +1,13 @@
os: unstable
clone_folder: c:\projects\uhk-agent
environment:
GH_TOKEN:
secure: 3IebpEKmC39codi1wT6dXx8mql4/mCL1JzZ7lir7GQ5MWRnCxlED2OXbiKHHigDV
CSC_LINK: c:\projects\uhk-agent\scripts\certs\windows-cert.p12
matrix:
- nodejs_version: "8"
- nodejs_version: "10.11.0"
cache:
- node_modules -> package.json
@@ -18,7 +21,6 @@ shallow_clone: true
install:
- ps: Install-Product node $env:nodejs_version
- npm i -g npm@5.6.0
- choco install chromium
- set CI=true
- set PATH=%APPDATA%\npm;%PATH%

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 392 B

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 546 B

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 660 B

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 966 B

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 20 KiB

112
cla/cla-1.0.0.md Normal file
View File

@@ -0,0 +1,112 @@
# Contributor Agreement
## Individual Contributor Exclusive License Agreement version 1.0.0
Thank you for your interest in contributing to Ultimate Gadget Laboratories Kft.'s Ultimate Hacking Keyboard Agent ("We" or "Us").
The purpose of this contributor agreement ("Agreement") is to clarify and document the rights granted by contributors to Us.
By signing this Agreement, you agree that the following terms apply to all of your past, present and future contributions to the project.
## How to use this Contributor Agreement
If You are an employee and have created the Contribution as part of your employment, You need to have Your employer approve this Agreement or sign the Entity version of this document. If You do not own the Copyright in the entire work of authorship, any other author of the Contribution should also sign this in any event, please contact Us at support@UltimateHackingKeyboard.com
## 1. Definitions
**"You"** means the individual Copyright owner who Submits a Contribution to Us.
**"Contribution"** means any original work of authorship, including any original modifications or additions to an existing work of authorship, Submitted by You to Us, in which You own the Copyright.
**"Copyright"** means all rights protecting works of authorship, including copyright, moral and neighboring rights, as appropriate, for the full term of their existence.
**"Material"** means the software or documentation made available by Us to third parties. When this Agreement covers more than one software project, the Material means the software or documentation to which the Contribution was Submitted. After You Submit the Contribution, it may be included in the Material.
**"Submit"** means any act by which a Contribution is transferred to Us by You by means of tangible or intangible media, including but not limited to electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Us, but excluding any transfer that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
**"Documentation"** means any non-software portion of a Contribution.
## 2. License grant
### 2.1 Copyright license to Us
Subject to the terms and conditions of this Agreement, You hereby grant to Us a worldwide, royalty-free, exclusive, perpetual and irrevocable (except as stated in Section 8.2) license, with the right to transfer an unlimited number of non-exclusive licenses or to grant sublicenses to third parties, under the Copyright covering the Contribution to use the Contribution by all means, including, but not limited to:
* publish the Contribution,
* modify the Contribution,
* prepare derivative works based upon or containing the Contribution and/or to combine the Contribution with other Materials,
* reproduce the Contribution in original or modified form,
* distribute, to make the Contribution available to the public, display and publicly perform the Contribution in original or modified form.
### 2.2 Moral rights
Moral Rights remain unaffected to the extent they are recognized and not waivable by applicable law.
### 2.3 Copyright license back to You
Upon such grant of rights to Us, We immediately grant to You a worldwide, royalty-free, non-exclusive, perpetual and irrevocable license, with the right to transfer an unlimited number of non-exclusive licenses or to grant sublicenses to third parties, under the Copyright covering the Contribution to use the Contribution by all means, including, but not limited to:
* publish the Contribution,
* modify the Contribution,
* prepare derivative works based upon or containing the Contribution and/or to combine the Contribution with other Materials,
* reproduce the Contribution in original or modified form,
* distribute, to make the Contribution available to the public, display and publicly perform the Contribution in original or modified form.
This license back is limited to the Contribution and does not provide any rights to the Material.
## 3. Patents
### 3.1 Patent license
Subject to the terms and conditions of this Agreement You hereby grant to Us and to recipients of Materials distributed by Us a worldwide, royalty-free, non-exclusive, perpetual and irrevocable (except as stated in Section 3.2) patent license, with the right to transfer an unlimited number of non-exclusive licenses or to grant sublicenses to third parties, to make, have made, use, sell, offer for sale, import and otherwise transfer the Contribution and the Contribution in combination with any Material (and portions of such combination). This license applies to all patents owned or controlled by You, whether already acquired or hereafter acquired, that would be infringed by making, having made, using, selling, offering for sale, importing or otherwise transferring of Your Contribution(s) alone or by combination of Your Contribution(s) with any Material.
### 3.2 Revocation of patent license
You reserve the right to revoke the patent license stated in section 3.1 if We make any infringement claim that is targeted at your Contribution and not asserted for a Defensive Purpose. An assertion of claims of the Patents shall be considered for a "Defensive Purpose" if the claims are asserted against an entity that has filed, maintained, threatened, or voluntarily participated in a patent infringement lawsuit against Us or any of Our licensees.
## 4. Disclaimer
THE CONTRIBUTION IS PROVIDED "AS IS". MORE PARTICULARLY, ALL EXPRESS OR IMPLIED WARRANTIES INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTY OF SATISFACTORY QUALITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE EXPRESSLY DISCLAIMED BY YOU TO US AND BY US TO YOU. TO THE EXTENT THAT ANY SUCH WARRANTIES CANNOT BE DISCLAIMED, SUCH WARRANTY IS LIMITED IN DURATION AND EXTENT TO THE MINIMUM PERIOD AND EXTENT PERMITTED BY LAW.
## 5. Consequential damage waiver
TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL YOU OR WE BE LIABLE FOR ANY LOSS OF PROFITS, LOSS OF ANTICIPATED SAVINGS, LOSS OF DATA, INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL AND EXEMPLARY DAMAGES ARISING OUT OF THIS AGREEMENT REGARDLESS OF THE LEGAL OR EQUITABLE THEORY (CONTRACT, TORT OR OTHERWISE) UPON WHICH THE CLAIM IS BASED.
## 6. Approximation of disclaimer and damage waiver
IF THE DISCLAIMER AND DAMAGE WAIVER MENTIONED IN SECTION 4. AND SECTION 5. CANNOT BE GIVEN LEGAL EFFECT UNDER APPLICABLE LOCAL LAW, REVIEWING COURTS SHALL APPLY LOCAL LAW THAT MOST CLOSELY APPROXIMATES AN ABSOLUTE WAIVER OF ALL CIVIL OR CONTRACTUAL LIABILITY IN CONNECTION WITH THE CONTRIBUTION.
## 7. Term
7.1 This Agreement shall come into effect upon Your acceptance of the terms and conditions.
7.2 In the event of a termination of this Agreement Sections 4, 5, 6, 7 and 8 shall survive such termination and shall remain in full force thereafter. For the avoidance of doubt, (sub)licenses that have already been granted for Contributions at the date of the termination shall remain in full force after the termination of this Agreement.
## 8. Miscellaneous
8.1 This Agreement and all disputes, claims, actions, suits or other proceedings arising out of this agreement or relating in any way to it shall be governed by the laws of Hungary excluding its private international law provisions.
8.2 This Agreement sets out the entire agreement between You and Us for Your Contributions to Us and overrides all other agreements or understandings.
8.3 In case of Your death, this agreement shall continue with Your heirs. In case of more than one heir, all heirs must exercise their rights through a commonly authorized person.
8.4 If any provision of this Agreement is found void and unenforceable, such provision will be replaced to the extent possible with a provision that comes closest to the meaning of the original provision and that is enforceable. The terms and conditions set forth in this Agreement shall apply notwithstanding any failure of essential purpose of this Agreement or any limited remedy to the maximum extent possible under law.
8.5 You agree to notify Us of any facts or circumstances of which you become aware that would make this Agreement inaccurate in any respect.
## 9. Signatures
I have read this Agreement, and fully agree to it by signing it with my GitHub username:
- @attilacsanyi
- @cokert
- @csanyiarpad
- @dgyimesi
- @eltang
- @ert78gb
- @fjozsef
- @laxu
- @mondalaci
- @NejcZdovc
- @spuder
- @srang
- @Lauszus

19549
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,11 @@
"private": true,
"author": "Ultimate Gadget Laboratories",
"main": "electron/dist/electron-main.js",
"version": "1.0.4",
"version": "1.2.13",
"firmwareVersion": "8.5.4",
"deviceProtocolVersion": "4.4.0",
"userConfigVersion": "4.0.1",
"hardwareConfigVersion": "1.0.0",
"description": "Agent is the configuration application of the Ultimate Hacking Keyboard.",
"repository": {
"type": "git",
@@ -11,52 +15,71 @@
},
"license": "GPL-3.0",
"engines": {
"node": ">=8.9.1 <9.0.0",
"npm": ">=5.6.0 <6.0.0"
"node": ">=10.2.1 <11.0.0",
"npm": ">=6.4.0 <7.0.0"
},
"devDependencies": {
"@types/decompress": "4.2.0",
"@types/electron-devtools-installer": "2.0.2",
"@types/electron-settings": "3.0.0",
"@types/fs-extra": "4.0.5",
"@types/jasmine": "2.6.0",
"@types/file-saver": "0.0.1",
"@types/fs-extra": "8.0.0",
"@types/jasmine": "3.3.12",
"@types/jasminewd2": "2.0.3",
"@types/jquery": "3.3.29",
"@types/jsonfile": "4.0.1",
"@types/lodash": "4.14.136",
"@types/node": "8.0.53",
"@types/node-hid": "0.5.2",
"@types/node-hid": "0.7.0",
"@types/request": "2.0.8",
"@types/usb": "1.1.3",
"@types/semver": "5.5.0",
"@types/tmp": "0.0.33",
"autoprefixer": "6.5.3",
"buffer": "5.0.6",
"copy-webpack-plugin": "4.0.1",
"check-node-version": "^3.2.0",
"copy-webpack-plugin": "5.0.0",
"copyfiles": "^2.0.0",
"core-js": "2.4.1",
"cross-env": "5.0.5",
"decompress": "4.2.0",
"decompress-tarbz2": "^4.1.1",
"decompress-tarbz2": "4.1.1",
"devtron": "1.4.0",
"electron": "1.7.5",
"electron-builder": "19.45.5",
"electron-debug": "1.4.0",
"electron-devtools-installer": "2.2.0",
"electron-log": "2.2.9",
"electron-rebuild": "1.6.0",
"electron-settings": "3.1.2",
"electron": "4.2.8",
"electron-builder": "20.34.0",
"electron-debug": "1.5.0",
"electron-devtools-installer": "2.2.3",
"electron-log": "2.2.16",
"electron-rebuild": "1.8.5",
"electron-settings": "3.1.4",
"electron-updater": "2.21.4",
"exports-loader": "0.6.3",
"file-loader": "0.10.0",
"fs-extra": "4.0.2",
"fs-extra": "8.1.0",
"gh-pages": "2.0.1",
"html-webpack-plugin": "3.2.0",
"jasmine": "3.4.0",
"jasmine-core": "3.4.0",
"jasmine-node": "3.0.0",
"jasmine-ts": "0.3.0",
"jsonfile": "4.0.0",
"lerna": "2.0.0",
"mkdirp": "0.5.1",
"lerna": "3.16.4",
"lodash": "4.17.15",
"node-hid": "0.7.8",
"npm-run-all": "4.0.2",
"nrf-intel-hex": "1.3.0",
"postcss-url": "8.0.0",
"pre-commit": "1.2.2",
"request": "2.83.0",
"request": "2.88.0",
"rimraf": "2.6.1",
"standard-version": "4.2.0",
"stylelint": "7.13.0",
"svg-sprite": "1.3.7",
"ts-loader": "2.3.1",
"ts-node": "3.0.4",
"tslint": "5.5.0",
"typescript": "2.5.2",
"webpack": "2.4.1"
"source-map-support": "0.5.9",
"stylelint": "10.0.0",
"svg-sprite": "1.5.0",
"ts-loader": "5.3.3",
"ts-node": "8.3.0",
"tslint": "5.18.0",
"typescript": "3.5.3",
"webpack": "4.29.5",
"webpack-cli": "3.2.3"
},
"pre-commit": [
"precommit-msg"
@@ -64,34 +87,20 @@
"scripts": {
"postinstall": "lerna bootstrap",
"precommit-msg": "Git precommit hook is running... & exit 0",
"test": "run-p -sn test:test-serializer test:uhk-common",
"test:test-serializer": "lerna exec --scope test-serializer npm test",
"test:uhk-common": "lerna exec --scope uhk-common npm test",
"test:uhk-web": "lerna exec --scope uhk-web 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: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:common build:usb build:web build:electron",
"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:common": "lerna exec --scope uhk-common npm run build",
"build:usb": "lerna exec --scope uhk-usb npm run build",
"test": "lerna run test",
"lint": "lerna run lint",
"e2e": "lerna run e2e --scope uhk-web",
"prebuild": "check-node-version --package",
"build": "lerna 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",
"electron:auto-write-config": "lerna exec --scope uhk-agent npm run auto-write-config",
"standard-version": "standard-version",
"electron:spe": "lerna exec --scope uhk-agent npm run electron:spe",
"pack": "node ./scripts/release.js",
"sprites": "node ./scripts/generate-svg-sprites",
"release": "node ./scripts/release.js",
"clean": "lerna exec rimraf ./node_modules ./dist"
},
"dependencies": {}
"clean": "lerna exec rimraf ./node_modules ./dist && rimraf ./node_modules ./dist ./tmp",
"predeploy-gh-pages": "lerna run build:web --scope=uhk-web",
"deploy-gh-pages": "gh-pages -d packages/uhk-web/dist"
}
}

1
packages/kboot/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './src';

View File

@@ -0,0 +1,8 @@
{
"spec_dir": "test",
"spec_files": [
"**/*[sS]pec.ts"
],
"stopSpecOnExpectationFailure": true,
"random": false
}

524
packages/kboot/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
{
"name": "kboot",
"main": "dist/index.js",
"version": "0.0.0",
"description": "Javascript implementation of the Kinetis Bootloader protocol",
"author": "Ultimate Gadget Laboratories",
"repository": {
"type": "git",
"url": "git@github.com:UltimateHackingKeyboard/agent.git"
},
"license": "GPL-3.0",
"engines": {
"node": ">=8.12.0 <9.0.0",
"npm": ">=6.4.1 <7.0.0"
},
"dependencies": {
"debug": "^4.1.1",
"byte-data": "^16.0.3",
"tslib": "^1.10.0",
"node-hid": ">= 0.7.3"
},
"peer-dependencies": {},
"scripts": {
"build": "tsc --project ./src/tsconfig.json",
"lint": "tslint --project tsconfig.json"
}
}

65
packages/kboot/readme.md Normal file
View File

@@ -0,0 +1,65 @@
Javascript implementation of the Kinetis Bootloader protocol
============================================================
Based on the [Kinetis Bootloader v2.0.0 Reference Manual](https://github.com/UltimateHackingKeyboard/bootloader/blob/master/doc/Kinetis%20Bootloader%20v2.0.0%20Reference%20Manual.pdf)
## Supported communication channels/protocols
- [x] USB
- [ ] I2C
- [ ] SPI
- [ ] CAN
- [ ] UART
## Supported Commands
We implemented only the commands that is used in UHK software.
If someone needs other commands, (s)he can easily implement it based on existing.
- [x] GetProperty
- [ ] SetProperty
- [ ] FlashEraseAll
- [x] FlashEraseRegion
- [x] FlashEraseAllUnsecure
- [x] ReadMemory
- [x] WriteMemory
- [ ] FillMemory
- [x] FlashSecurityDisable
- [ ] Execute
- [ ] Call
- [x] Reset
- [ ] FlashProgramOnce
- [ ] FlashReadOnce
- [ ] FlashReadResource
- [ ] ConfigureQuadSpi
- [ ] ReliableUpdate
- [x] ConfigureI2c
- [ ] ConfigureSpi
- [ ] ConfigureCan
## How to use
```Typescript
// Initialize peripheral
const usbPeripheral = new UsbPeripheral({ productId: 1, vendorId: 1 });
// Initialize Kboot
const kboot = new KBoot(usbPeripheral);
// Call the command
const version = await kboot.getBootloaderVersion();
// ... more commands
// Close the communication channel. Release resources
kboot.close();
```
If you have to communicate other I2C device over USB call `kboot.configureI2c(i2cId)` before the command.
```Typescript
const usbPeripheral = new UsbPeripheral({ productId: 1, vendorId: 1 });
const kboot = new KBoot(usbPeripheral);
// Get the bootloader version of I2C device
await kboot.configureI2c(i2cId);
const version = await kboot.getBootloaderVersion();
```
## TODO
- [ ] Improve exception handling

View File

@@ -0,0 +1,2 @@
export const DEFAULT_USB_PID = 0x0073;
export const DEFAULT_USB_VID = 0x15A2;

View File

@@ -0,0 +1,23 @@
export enum Commands {
FlashEraseAll = 0x01,
FlashEraseRegion = 0x02,
ReadMemory = 0x03,
WriteMemory = 0x04,
FillMemory = 0x05,
FlashSecurityDisable = 0x06,
GetProperty = 0x07,
ReceiveSBFile = 0x08,
Execute = 0x09,
Call = 0x0A,
Reset = 0x0B,
SetProperty = 0x0C,
FlashEraseAllUnsecure = 0x0D,
FlashProgramOnce = 0x0E,
FlashReadOnce = 0x0F,
FlashReadResource = 0x10,
ConfigureQuadSpi = 0x11,
ReliableUpdate = 0x12,
ConfigureI2c = 0xc1,
ConfigureSpi = 0xc2,
ConfigureCan = 0xc3
}

View File

@@ -0,0 +1,5 @@
export * from './commands';
export * from './memory-ids';
export * from './properties';
export * from './response-codes';
export * from './response-tags';

View File

@@ -0,0 +1,5 @@
export enum MemoryIds {
Internal = 0,
Spi0 = 1,
ExecuteOnly = 0x10
}

View File

@@ -0,0 +1,26 @@
export enum Properties {
BootloaderVersion = 0x01,
AvailablePeripherals = 0x02,
FlashStartAddress = 0x03,
FlashSize = 0x04,
FlashSectorSize = 0x05,
FlashBlockCount = 0x06,
AvailableCommands = 0x07,
CrcCheckStatus = 0x08,
VerifyWrites = 0x0A,
MaxPacketSize = 0x0B,
ReservedRegions = 0x0C,
ValidateRegions = 0x0D,
RAMStartAddress = 0x0E,
RAMSize = 0x0F,
SystemDeviceIdent = 0x10,
FlashSecurityState = 0x11,
UniqueDeviceIdent = 0x12,
FlashFacSupport = 0x13,
FlashAccessSegmentSize = 0x14,
FlashAccessSegmentCount = 0x15,
FlashReadMargin = 0x16,
QspiInitStatus = 0x17,
TargetVersion = 0x18,
ExternalMemoryAttributes = 0x19
}

View File

@@ -0,0 +1,76 @@
export enum ResponseCodes {
// Generic status codes.
Success = 0,
Fail = 1,
ReadOnly = 2,
OutOfRange = 3,
InvalidArgument = 4,
// Flash driver errors.
FlashSizeError = 100,
FlashAlignmentError = 101,
FlashAddressError = 102,
FlashAccessError = 103,
FlashProtectionViolation = 104,
FlashCommandFailure = 105,
FlashUnknownProperty = 106,
// I2C driver errors.
I2C_SlaveTxUnderrun = 200,
I2C_SlaveRxOverrun = 201,
I2C_AribtrationLost = 202,
// SPI driver errors.
SPI_SlaveTxUnderrun = 300,
SPI_SlaveRxOverrun = 301,
// QuadSPI driver errors
QSPI_FlashSizeError = 400,
QSPI_FlashAlignmentError = 401,
QSPI_FlashAddressError = 402,
QSPI_FlashCommandFailure = 403,
QSPI_FlashUnknownProperty = 404,
QSPI_NotConfigured = 405,
QSPI_CommandNotSupported = 406,
// Bootloader errors.
UnknownCommand = 10000,
SecurityViolation = 10001,
AbortDataPhase = 10002,
PingError = 10003,
NoResponse = 10004,
NoResponseExpected = 10005,
// SB loader errors.
RomLdrSectionOverrun = 10100,
RomLdrSignature = 10101,
RomLdrSectionLength = 10102,
RomLdrUnencryptedOnly = 10103,
RomLdrEOFReached = 10104,
RomLdrChecksum = 10105,
RomLdrCrc32Error = 10106,
RomLdrUnknownCommand = 10107,
RomLdrIdNotFound = 10108,
RomLdrDataUnderrun = 10109,
RomLdrJumpReturned = 10110,
RomLdrCallFailed = 10111,
RomLdrKeyNotFound = 10112,
RomLdrSecureOnly = 10113,
// Memory interface errors.
MemoryRangeInvalid = 10200,
MemoryReadFailed = 10201,
MemoryWriteFailed = 10202,
// Property store errors.
UnknownProperty = 10300,
ReadOnlyProperty = 10301,
InvalidPropertyValue = 10302,
// Property store errors.
AppCrcCheckPassed = 10400,
AppCrcCheckFailed = 10401,
AppCrcCheckInactive = 10402,
AppCrcCheckInvalid = 10403,
AppCrcCheckOutOfRange = 10404
}

View File

@@ -0,0 +1,7 @@
export enum ResponseTags {
Generic = 0xA0,
ReadMemory = 0xA3,
Property = 0xA7,
FlashReadOnce = 0xAF,
FlashReadResource = 0xB0
}

View File

@@ -0,0 +1,6 @@
export * from './kboot';
export * from './enums';
export * from './models';
export * from './peripheral';
export * from './usb-peripheral';
export * from './util';

194
packages/kboot/src/kboot.ts Normal file
View File

@@ -0,0 +1,194 @@
import { debug } from 'debug';
import { pack } from 'byte-data';
import { Peripheral } from './peripheral';
import { Commands, MemoryIds, Properties, ResponseCodes, ResponseTags } from './enums';
import { BootloaderVersion, CommandOption, CommandResponse, DataOption } from './models';
const logger = debug('kboot');
export class KBoot {
constructor(private peripheral: Peripheral) {
}
open(): void {
this.peripheral.open();
}
close(): void {
this.peripheral.close();
}
// ================= Read properties ==================
async getProperty(property: Properties, memoryId = MemoryIds.Internal): Promise<CommandResponse> {
const command: CommandOption = {
command: Commands.GetProperty,
params: [
...pack(property, { bits: 32 }),
...pack(memoryId, { bits: 32 })
]
};
const response = await this.peripheral.sendCommand(command);
if (response.tag !== ResponseTags.Property) {
throw new Error('Response tag is not property response');
}
if (response.code === ResponseCodes.UnknownProperty) {
throw new Error('Unknown property!');
}
if (response.code !== ResponseCodes.Success) {
throw new Error(`Unknown error. Error code:${response.code}`);
}
return response;
}
async getBootloaderVersion(): Promise<BootloaderVersion> {
const response = await this.getProperty(Properties.BootloaderVersion);
const version: BootloaderVersion = {
bugfix: response.raw[12],
minor: response.raw[13],
major: response.raw[14],
protocolName: String.fromCharCode(response.raw[15])
};
logger('bootloader version %o');
return version;
}
// TODO: Implement other get/set property wrappers
// ================= End read properties ==================
async flashSecurityDisable(key: number[]): Promise<void> {
if (key.length !== 8) {
throw new Error('Flash security key must be 8 byte');
}
const command: CommandOption = {
command: Commands.FlashSecurityDisable,
params: [...key]
};
const response = await this.peripheral.sendCommand(command);
if (response.tag !== ResponseTags.Generic) {
throw new Error('Response tag is not generic response');
}
if (response.code !== ResponseCodes.Success) {
throw new Error(`Can not disable flash security`);
}
}
async flashEraseRegion(startAddress: number, count: number): Promise<void> {
const command: CommandOption = {
command: Commands.FlashEraseRegion,
params: [
...pack(startAddress, { bits: 32 }),
...pack(count, { bits: 32 })
]
};
const response = await this.peripheral.sendCommand(command);
if (response.tag !== ResponseTags.Generic) {
throw new Error('Response tag is not generic response');
}
if (response.code !== ResponseCodes.Success) {
throw new Error(`Can not disable flash security`);
}
}
async flashEraseAllUnsecure(): Promise<void> {
const command: CommandOption = {
command: Commands.FlashEraseAllUnsecure,
params: []
};
const response = await this.peripheral.sendCommand(command);
if (response.tag !== ResponseTags.Generic) {
throw new Error('Response tag is not generic response');
}
if (response.code !== ResponseCodes.Success) {
throw new Error(`Can not disable flash security`);
}
}
async readMemory(startAddress: number, count: number): Promise<any> {
return this.peripheral.readMemory(startAddress, count);
}
async writeMemory(options: DataOption): Promise<void> {
return this.peripheral.writeMemory(options);
}
/**
* Reset the bootloader
*/
async reset(): Promise<void> {
const command: CommandOption = {
command: Commands.Reset,
params: []
};
let response: CommandResponse;
try {
response = await this.peripheral.sendCommand(command);
} catch (error) {
if (error.message === 'could not read from HID device') {
logger('Ignoring missing response from reset command.');
this.close();
return;
}
throw error;
}
if (response.tag !== ResponseTags.Generic) {
throw new Error('Response tag is not generic response');
}
if (response.code !== ResponseCodes.Success) {
throw new Error(`Unknown error. Error code:${response.code}`);
}
}
/**
* Call it before send data to I2C
* @param address - The address of the I2C
* @param [speed=64] - Speed of the I2C
*/
async configureI2c(address: number, speed = 64): Promise<void> {
if (address > 127) {
throw new Error('Only 7-bit i2c address is supported');
}
const command: CommandOption = {
command: Commands.ConfigureI2c,
params: [
...pack(address, { bits: 32 }),
...pack(speed, { bits: 32 })
]
};
const response = await this.peripheral.sendCommand(command);
if (response.tag !== ResponseTags.Generic) {
throw new Error('Response tag is not generic response');
}
if (response.code !== ResponseCodes.Success) {
throw new Error(`Unknown error. Error code:${response.code}`);
}
}
}

View File

@@ -0,0 +1,6 @@
export interface BootloaderVersion {
major: number;
minor: number;
bugfix: number;
protocolName: string;
}

View File

@@ -0,0 +1,7 @@
import { Commands } from '../enums';
export interface CommandOption {
command: Commands;
hasDataPhase?: boolean;
params?: number[];
}

View File

@@ -0,0 +1,7 @@
import { ResponseCodes, ResponseTags } from '../enums';
export interface CommandResponse {
tag: ResponseTags;
code: ResponseCodes;
raw: Buffer;
}

View File

@@ -0,0 +1,4 @@
export interface DataOption {
startAddress: number;
data: Buffer;
}

View File

@@ -0,0 +1,5 @@
export * from './bootloader-version';
export * from './command-option';
export * from './command-response';
export * from './data-option';
export * from './usb';

View File

@@ -0,0 +1,7 @@
export interface USB {
vendorId: number;
productId: number;
interface?: number;
usage?: number;
usePage?: number;
}

View File

@@ -0,0 +1,13 @@
import { CommandOption, CommandResponse, DataOption } from './models';
export interface Peripheral {
open(): void;
close(): void;
sendCommand(options: CommandOption): Promise<CommandResponse>;
writeMemory(data: DataOption): Promise<void>;
readMemory(startAddress: number, count: number): Promise<Buffer>;
}

View File

@@ -0,0 +1,7 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"baseUrl": "./",
"outDir": "../dist"
}
}

View File

@@ -0,0 +1,275 @@
import { debug } from 'debug';
import { devices, HID } from 'node-hid';
import { pack } from 'byte-data';
import { Peripheral } from './peripheral';
import { CommandOption, CommandResponse, DataOption, USB } from './models';
import { convertLittleEndianNumber, convertToHexString, deviceFinder, encodeCommandOption } from './util';
import { decodeCommandResponse } from './util/usb/decode-command-response';
import { validateCommandParams } from './util/usb/encode-command-option';
import { Commands, ResponseTags } from './enums';
import { snooze } from './util/snooze';
const logger = debug('kboot:usb');
const WRITE_DATA_STREAM_PACKAGE_LENGTH = 32;
export class UsbPeripheral implements Peripheral {
private _device: HID;
private _responseBuffer: Buffer;
private _dataBuffer: Buffer;
private _hidError: any;
constructor(private options: USB) {
logger('constructor options: %o', options);
}
open(): void {
this._hidError = undefined;
if (this._device) {
return;
}
logger('Available devices');
const device = devices()
.map(x => {
logger('%o', x);
return x;
})
.find(deviceFinder(this.options));
if (!device) {
logger('USB device can not be found %o', this.options);
throw new Error('USB device can not be found');
}
this._responseBuffer = Buffer.alloc(0);
this._dataBuffer = Buffer.alloc(0);
this._device = new HID(device.path);
this._device.on('data', this._usbDataListener.bind(this));
this._device.on('error', this._usbErrorListener.bind(this));
}
close(): void {
if (this._device) {
this._device.close();
this._device = undefined;
}
}
async sendCommand(options: CommandOption): Promise<CommandResponse> {
validateCommandParams(options.params);
const data = encodeCommandOption(options);
this._send(data);
return this._getNextCommandResponse();
}
writeMemory(option: DataOption): Promise<void> {
return new Promise<void>(async (resolve, reject) => {
try {
const command: CommandOption = {
command: Commands.WriteMemory,
hasDataPhase: true,
params: [
...pack(option.startAddress, { bits: 32 }),
...pack(option.data.length, { bits: 32 })
]
};
const firsCommandResponse = await this.sendCommand(command);
if (firsCommandResponse.tag !== ResponseTags.Generic) {
logger('Invalid write memory response! %o', firsCommandResponse);
return reject(new Error('Invalid write memory response!'));
}
if (firsCommandResponse.code !== 0) {
logger('Non zero write memory response! %o', firsCommandResponse);
return reject(new Error(`Non zero write memory response! Response code: ${firsCommandResponse.code}`));
}
for (let i = 0; i < option.data.length; i = i + WRITE_DATA_STREAM_PACKAGE_LENGTH) {
if (this._hidError) {
logger('Throw USB error %O', this._hidError);
return reject(new Error('USB error while write data'));
}
const slice = option.data.slice(i, i + WRITE_DATA_STREAM_PACKAGE_LENGTH);
const writeData = [
2, // USB channel
0,
slice.length,
0, // TODO: What is it?
...slice
];
logger('send data %o', convertToHexString(writeData));
this._device.write(writeData);
}
const secondCommandResponse = await this._getNextCommandResponse();
if (secondCommandResponse.tag !== ResponseTags.Generic) {
logger('Invalid write memory final response %o', secondCommandResponse);
return reject(new Error('Invalid write memory final response!'));
}
if (secondCommandResponse.code !== 0) {
logger('Non zero write memory final response %o', secondCommandResponse);
const msg = `Non zero write memory final response! Response code: ${secondCommandResponse.code}`;
return reject(new Error(msg));
}
resolve();
} catch (err) {
logger('Can not write memory data %O', err);
reject(err);
}
});
}
readMemory(startAddress: number, count: number): Promise<Buffer> {
return new Promise<Buffer>(async (resolve, reject) => {
try {
const command: CommandOption = {
command: Commands.ReadMemory,
params: [
...pack(startAddress, { bits: 32 }),
...pack(count, { bits: 32 })
]
};
this._resetDataBuffer();
this._resetResponseBuffer();
const firsCommandResponse = await this.sendCommand(command);
if (firsCommandResponse.tag !== ResponseTags.ReadMemory) {
logger('Invalid read memory response %o', firsCommandResponse);
return reject(new Error('Invalid read memory response!'));
}
if (firsCommandResponse.code !== 0) {
logger('Non zero read memory response %o', firsCommandResponse);
return reject(new Error(`Non zero read memory response! Response code: ${firsCommandResponse.code}`));
}
const byte4Number = firsCommandResponse.raw.slice(12, 15);
const arrivingData = convertLittleEndianNumber(byte4Number);
const memoryDataBuffer = await this._readFromDataStream(arrivingData);
const secondCommandResponse = await this._getNextCommandResponse();
if (secondCommandResponse.tag !== ResponseTags.Generic) {
logger('Invalid read memory final response %o', secondCommandResponse);
return reject(new Error('Invalid read memory final response!'));
}
if (secondCommandResponse.code !== 0) {
logger('Non zero read memory final response %o', secondCommandResponse);
const msg = `Non zero read memory final response! Response code: ${secondCommandResponse.code}`;
return reject(new Error(msg));
}
resolve(memoryDataBuffer);
} catch (error) {
logger('Read memory error %O', error);
reject(error);
}
});
}
private _send(data: number[]): void {
this.open();
logger('send data %o', `<${convertToHexString(data)}>`);
this._device.write(data);
}
private _usbDataListener(data: Buffer): void {
logger('received data %o', `[${convertToHexString(data)}]`);
const channel = data[0];
switch (channel) {
case 3:
this._responseBuffer = Buffer.concat([this._responseBuffer, data]);
break;
case 4:
this._dataBuffer = Buffer.concat([this._dataBuffer, data]);
break;
default:
logger('Unknown USB channel %o', channel);
break;
}
}
private _usbErrorListener(error: any): void {
logger('USB stream error %O', error);
this._hidError = error;
}
private _readFromCommandStream(byte = 36, timeout = 15000): Promise<Buffer> {
return this._readFromBuffer('_responseBuffer', byte, timeout);
}
private _readFromDataStream(byte = 36, timeout = 15000): Promise<Buffer> {
return this._readFromBuffer('_dataBuffer', byte, timeout);
}
private _readFromBuffer(bufferName: string, byte: number, timeout: number): Promise<Buffer> {
return new Promise<Buffer>(async (resolve, reject) => {
const startTime = new Date();
while (startTime.getTime() + timeout > new Date().getTime()) {
if (this._hidError) {
const err = this._hidError;
return reject(err);
}
const buffer: Buffer = this[bufferName];
if (buffer.length >= byte) {
const data = buffer.slice(0, byte);
if (buffer.length === byte) {
this[bufferName] = Buffer.alloc(0);
} else {
const newDataBuffer = Buffer.alloc(buffer.length - byte);
buffer.copy(newDataBuffer, 0, byte);
this[bufferName] = newDataBuffer;
}
logger(`read from ${bufferName}: %O`, convertToHexString(data));
return resolve(data);
}
await snooze(100);
}
logger('Timeout while try to read from buffer');
reject(new Error('Timeout while try to read from buffer'));
});
}
private _resetDataBuffer(): void {
this._dataBuffer = Buffer.alloc(0);
}
private _resetResponseBuffer(): void {
this._responseBuffer = Buffer.alloc(0);
}
private async _getNextCommandResponse(): Promise<CommandResponse> {
const response = await this._readFromCommandStream();
const commandResponse = decodeCommandResponse(response);
logger('next command response: %o', commandResponse);
return commandResponse;
}
}

View File

@@ -0,0 +1,6 @@
export const encodeStringToParams = (data: string): Int8Array => {
// const buffer = Buffer.from(data);
// return new Int8Array(buffer, 0);
return new Int8Array(0);
};

View File

@@ -0,0 +1,22 @@
export * from './encode-string-to-parameters';
export * from './response-parser';
export * from './usb';
export const convertToHexString = (arr: number[] | Buffer): string => {
let str = '';
for (const n of arr) {
let hex = n.toString(16);
if (hex.length < 2) {
hex = '0' + hex;
}
if (str.length > 0) {
str += ' ';
}
str += hex;
}
return str;
};

View File

@@ -0,0 +1,21 @@
import { ResponseCodes, ResponseTags } from '../enums';
export const convertLittleEndianNumber = (data: Buffer): number => {
let value = 0;
for (let i = 0; i < data.length; i++) {
value += data[i] << (8 * i);
}
return value;
};
export const getResponseCode = (response: Buffer): ResponseCodes => {
const data = response.slice(8, 11);
return convertLittleEndianNumber(data);
};
export const getResponseTag = (response: Buffer): ResponseTags => {
return response[4];
};

View File

@@ -0,0 +1 @@
export const snooze = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

View File

@@ -0,0 +1,18 @@
import { CommandResponse } from '../../models';
import { getResponseCode, getResponseTag } from '../response-parser';
export const decodeCommandResponse = (response: Buffer): CommandResponse => {
if (response.length < 8) {
throw new Error('Invalid response length!');
}
if (response[0] !== 3) {
throw new Error(`Invalid response command channel!`);
}
return {
code: getResponseCode(response),
tag: getResponseTag(response),
raw: response
};
};

View File

@@ -0,0 +1,22 @@
import { isNullOrUndefined } from 'util';
import { Device } from 'node-hid';
import { USB } from '../../models';
export const deviceFinder = (usb: USB) => {
return (device: Device): boolean => {
if (device.productId !== usb.productId) {
return false;
}
if (device.vendorId !== usb.vendorId) {
return false;
}
// TODO: Add interface, usage and usePage filtering
// if (!isNullOrUndefined(usb.interface) && device.interface !== -1 && device.interface === usb.interface) {
// return true;
// }
return true;
};
};

View File

@@ -0,0 +1,46 @@
import { isNullOrUndefined } from 'util';
import { pack } from 'byte-data';
import { CommandOption } from '../../models';
/**
* Encode the USB Command.
* @param option
*/
export const encodeCommandOption = (option: CommandOption): number[] => {
const payload = [
option.command,
option.hasDataPhase ? 1 : 0,
0, // Reserved. Should be 0
option.params ? option.params.length / 4 >> 0 : 0 // number of parameters
];
if (option.params) {
payload.push(...option.params);
}
const header = [
1, // Communication channel
0, // TODO: What is it?
...pack(payload.length, { bits: 16 }) // payload length in 2 byte
];
const placeholders = new Array(32 - payload.length)
.fill(0);
return [...header, ...payload, ...placeholders];
};
export const validateCommandParams = (params: any[]): void => {
if (isNullOrUndefined(params)) {
return;
}
if (!Array.isArray(params)) {
throw new Error('Command parameters must be an array!');
}
if (params.length > 28) {
throw new Error('Maximum 7 (28 bytes) command parameters allowed!');
}
};

View File

@@ -0,0 +1,2 @@
export { deviceFinder } from './device-finder';
export { encodeCommandOption } from './encode-command-option';

View File

@@ -0,0 +1,36 @@
import { BootloaderVersion, CommandResponse, Commands, KBoot, Peripheral, Properties, ResponseCodes, ResponseTags } from '../src';
import { TestPeripheral } from './test-peripheral';
describe('kboot', () => {
let kboot: KBoot;
let testPeripheral: Peripheral;
beforeEach(() => {
testPeripheral = new TestPeripheral();
kboot = new KBoot(testPeripheral);
});
describe('getBootloaderVersion', () => {
it('should works', async () => {
const sendCommandResponse: CommandResponse = {
code: ResponseCodes.Success,
tag: ResponseTags.Property,
// tslint:disable-next-line:max-line-length
raw: Buffer.from([0x03, 0x00, 0x0c, 0x00, 0xa7, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
};
spyOn(testPeripheral, 'sendCommand').and.returnValue(Promise.resolve(sendCommandResponse));
const version = await kboot.getBootloaderVersion();
const expectedVersion: BootloaderVersion = {
protocolName: 'K',
major: 2,
minor: 0,
bugfix: 0
};
expect(version).toEqual(expectedVersion);
expect(testPeripheral.sendCommand).toHaveBeenCalledWith({
command: Commands.GetProperty,
params: [1, 0, 0, 0, 0, 0, 0, 0]
});
});
});
});

View File

@@ -0,0 +1,27 @@
import { CommandOption, CommandResponse, DataOption, Peripheral, ResponseCodes, ResponseTags } from '../src';
export class TestPeripheral implements Peripheral {
close(): void {
}
open(): void {
}
sendCommand(options: CommandOption): Promise<CommandResponse> {
const response = {
tag: ResponseTags.Generic,
code: ResponseCodes.Success,
raw: Buffer.alloc(0)
};
return Promise.resolve(response);
}
writeMemory(data: DataOption): Promise<void> {
return Promise.resolve();
}
readMemory(startAddress: number, count: number): Promise<Buffer> {
return Promise.resolve(Buffer.alloc(0));
}
}

View File

@@ -0,0 +1,33 @@
import { execSync } from 'child_process';
import { join } from 'path';
import { readFileSync } from 'fs';
import * as MemoryMap from 'nrf-intel-hex';
export enum UhkReenumerationModes {
Bootloader = 'bootloader',
Buspal = 'buspal',
NormalKeyboard = 'normalKeyboard',
CompatibleKeyboard = 'compatibleKeyboard'
}
const USB_SCRIPTS_DIR = join(__dirname, '../../../usb');
export const reenumerate = (mode: UhkReenumerationModes): void => {
const reenumerateScriptFile = join(USB_SCRIPTS_DIR, 'reenumerate.js');
const command = [reenumerateScriptFile, mode.toString()].join(' ');
execSync(
command,
{
cwd: USB_SCRIPTS_DIR,
stdio: [0, 1, 2]
}
);
};
export const readBootloaderFirmwareFromHexFile = (): Map<any, any> => {
const hexFilePath = join(__dirname, '../../../../tmp/packages/firmware/devices/uhk60-right/firmware.hex');
const fileContent = readFileSync(hexFilePath, { encoding: 'utf8' });
const memoryMap = MemoryMap.fromHex(fileContent);
return memoryMap;
};

View File

@@ -0,0 +1,10 @@
import { encodeStringToParams } from '../../src/util';
describe('encodeStringToParams', () => {
xit('should convert 8 character to little endian 4 byte array', () => {
const result = encodeStringToParams('0403020108070605');
const expectedResult = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
expect(result).toEqual(expectedResult);
});
});

View File

@@ -0,0 +1,17 @@
import { getResponseCode, ResponseCodes } from '../../src';
describe('response-parser', () => {
describe('getResponseCode', () => {
it('should return with success', () => {
const buffer = Buffer.from([0x03, 0x00, 0x08, 0x00, 0xa7, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00]);
const responseCode = getResponseCode(buffer);
expect(responseCode).toEqual(ResponseCodes.Success);
});
it('should return with UnknownProperty', () => {
const buffer = Buffer.from([0x03, 0x00, 0x08, 0x00, 0xa7, 0x00, 0x00, 0x01, 0x3c, 0x28, 0x00, 0x00]);
const responseCode = getResponseCode(buffer);
expect(responseCode).toEqual(ResponseCodes.UnknownProperty);
});
});
});

View File

@@ -0,0 +1,13 @@
import { decodeCommandResponse } from '../../../src/util/usb/decode-command-response';
describe('decodeCommandResponse', () => {
it('should parse the command', () => {
const arr = '03 00 0c 00 a0 00 00 02 00 00 00 00 c1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00'
.split(' ');
const buffer = Buffer.from(arr);
const response = decodeCommandResponse(buffer);
expect(response.code).toEqual(0);
expect(response.tag).toEqual(0);
});
});

View File

@@ -0,0 +1,16 @@
import { CommandOption, Commands } from '../../../src';
import { encodeCommandOption } from '../../../src/util';
describe('usb encodeCommandOption', () => {
it('should convert correctly', () => {
const option: CommandOption = {
command: Commands.GetProperty,
params: [1, 0, 0, 0, 0, 0, 0, 0]
};
const result = encodeCommandOption(option);
// tslint:disable-next-line:max-line-length
const expected = [1, 0, 0x0c, 0, 0x07, 0x00, 0x00, 0x02, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
expect(result).toEqual(expected);
});
});

View File

@@ -0,0 +1,81 @@
import { reenumerate, UhkReenumerationModes, readBootloaderFirmwareFromHexFile } from './uhk-helpers';
import { BootloaderVersion, UsbPeripheral } from '../src';
import { KBoot } from '../src/kboot';
import { DataOption } from '../index';
xdescribe('UHK Integration tests', () => {
describe('bootloader', () => {
let usb: UsbPeripheral;
let kboot: KBoot;
beforeEach(() => {
reenumerate(UhkReenumerationModes.Bootloader);
usb = new UsbPeripheral({ vendorId: 0x1d50, productId: 0x6120 });
kboot = new KBoot(usb);
});
afterEach(() => {
if (usb) {
usb.close();
}
if (kboot) {
kboot.close();
}
});
it('get bootloader version', async () => {
const expectedVersion: BootloaderVersion = {
protocolName: 'K',
major: 2,
minor: 0,
bugfix: 0
};
const version = await kboot.getBootloaderVersion();
expect(version).toEqual(expectedVersion);
});
it('disable flash security', () => {
const backdoorKey = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
return kboot
.flashSecurityDisable(backdoorKey)
.catch(err => {
expect(err).toBeFalsy();
});
});
it('flash erase region', () => {
return kboot
.flashEraseRegion(0xc000, 475136)
.catch(err => {
expect(err).toBeFalsy();
});
});
it('read memory', () => {
const dataLength = 128;
return kboot
.readMemory(0xc000, dataLength)
.then((data: number[]) => {
expect(data).toBeTruthy();
expect(data.length).toEqual(dataLength);
})
.catch(err => {
expect(err).toBeFalsy();
});
});
it('write memory', async () => {
const bootloaderMemoryMap = readBootloaderFirmwareFromHexFile();
for (const [startAddress, data] of bootloaderMemoryMap.entries()) {
const dataOption: DataOption = {
startAddress,
data
};
await kboot.writeMemory(dataOption);
}
});
});
});

View File

@@ -0,0 +1,6 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": "./"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -14,18 +14,12 @@
"npm": ">=5.1.0 <6.0.0"
},
"dependencies": {
},
"devDependencies": {
"@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"
},
"devDependencies": {
},
"scripts": {
"test": "jasmine-ts --config=jasmine.json"
"test": "jasmine-ts --config=jasmine.json",
"lint": "tslint --project tsconfig.json"
}
}

View File

@@ -1,11 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"experimentalDecorators": true,
"typeRoots": [
"./node_modules/@types"
]
"experimentalDecorators": true
}
}

View File

@@ -1,28 +0,0 @@
// var webpack = require("webpack");
module.exports = {
entry: {
main: __dirname + '/test-serializer.ts'
},
target: 'node',
output: {
path: __dirname,
filename: "test-serializer.js"
},
resolve: {
extensions: ['.webpack.js', '.web.js', '.ts', '.js'],
modules: ['node_modules']
},
module: {
rules: [
{ test: /\.ts$/, loader: 'ts-loader', exclude: /node_modules/ }
]
},
plugins: [
// new webpack.optimize.UglifyJsPlugin({ minimize: true }),
],
node: {
fs: "empty"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -17,30 +17,23 @@
"command-line-args": "4.0.7",
"decompress": "4.2.0",
"decompress-bzip2": "4.0.0",
"electron": "1.7.9",
"electron-is-dev": "0.1.2",
"electron-log": "2.2.9",
"electron-rebuild": "1.6.0",
"electron-settings": "3.1.2",
"electron-updater": "2.15.0",
"node-hid": "0.5.4",
"node-hid": "0.7.8",
"sudo-prompt": "7.0.0",
"tmp": "0.0.33",
"tslib": "1.10.0",
"uhk-common": "^1.0.0",
"uhk-usb": "^1.0.0"
},
"devDependencies": {
"@types/decompress": "4.2.0",
"@types/node": "8.0.33",
"@types/tmp": "0.0.33"
},
"scripts": {
"start": "electron ./dist/electron-main.js",
"auto-write-config": "electron ./dist/electron-main.js --auto-write-config",
"build": "webpack && npm run install:build-deps && npm run build:usb && npm run download-firmware && npm run copy-blhost",
"start": "cross-env DEBUG=kboot* electron ./dist/electron-main.js",
"electron:spe": "electron ./dist/electron-main.js --spe",
"build": "webpack && npm run install:build-deps && npm run build:usb && npm run download-firmware && npm run copy-to-tmp-folder",
"build:usb": "electron-rebuild -w node-hid -p -m ./dist",
"lint": "tslint --project tsconfig.json",
"install:build-deps": "cd ./dist && npm i",
"download-firmware": "node ../../scripts/download-firmware.js",
"copy-blhost": "node ../../scripts/copy-blhost.js"
"copy-to-tmp-folder": "node ../../scripts/copy-to-tmp-folder.js"
}
}

View File

@@ -2,7 +2,7 @@
/// <reference path="./custom_types/command-line-args.d.ts"/>
import './polyfills';
import { app, BrowserWindow, ipcMain } from 'electron';
import { app, BrowserWindow } from 'electron';
import { autoUpdater } from 'electron-updater';
import * as path from 'path';
@@ -10,22 +10,22 @@ import * as url from 'url';
import * as commandLineArgs from 'command-line-args';
import { UhkHidDevice, UhkOperations } from 'uhk-usb';
// import { ElectronDataStorageRepositoryService } from './services/electron-datastorage-repository.service';
import { LogRegExps } from 'uhk-common';
import { CommandLineArgs, LogRegExps } from 'uhk-common';
import { DeviceService } from './services/device.service';
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 { UhkBlhost } from '../../uhk-usb/src';
import * as isDev from 'electron-is-dev';
import { CommandLineInputs } from './models/command-line-inputs';
import { setMenu } from './electron-menu';
import { loadWindowState, saveWindowState } from './util/window';
const optionDefinitions = [
{name: 'addons', type: Boolean},
{name: 'auto-write-config', type: Boolean}
{name: 'spe', type: Boolean} // simulate privilege escalation error
];
const options: CommandLineInputs = commandLineArgs(optionDefinitions);
const options: CommandLineArgs = commandLineArgs(optionDefinitions);
// import './dev-extension';
// require('electron-debug')({ showDevTools: true, enabled: true });
@@ -36,7 +36,6 @@ let win: Electron.BrowserWindow;
autoUpdater.logger = logger;
let deviceService: DeviceService;
let uhkBlhost: UhkBlhost;
let uhkHidDeviceService: UhkHidDevice;
let uhkOperations: UhkOperations;
let appUpdateService: AppUpdateService;
@@ -61,7 +60,13 @@ if (console.debug) {
};
}
const isSecondInstance = !app.requestSingleInstanceLock();
function createWindow() {
if (isSecondInstance) {
return;
}
logger.info('[Electron Main] Create new window.');
let packagesDir;
if (isDev) {
@@ -72,25 +77,34 @@ function createWindow() {
logger.info(`[Electron Main] packagesDir: ${packagesDir}`);
const loadedWindowState = loadWindowState();
// Create the browser window.
win = new BrowserWindow({
title: 'UHK Agent',
width: 1024,
height: 768,
x: loadedWindowState.x,
y: loadedWindowState.y,
width: loadedWindowState.width,
height: loadedWindowState.height,
webPreferences: {
nodeIntegration: true
},
icon: 'assets/images/agent-icon.png'
icon: path.join(__dirname, 'renderer/assets/images/agent-app-icon.png')
});
win.setMenuBarVisibility(false);
win.maximize();
uhkHidDeviceService = new UhkHidDevice(logger);
uhkBlhost = new UhkBlhost(logger, packagesDir);
uhkOperations = new UhkOperations(logger, uhkBlhost, uhkHidDeviceService, packagesDir);
deviceService = new DeviceService(logger, win, uhkHidDeviceService, uhkOperations);
if (loadedWindowState.isFullScreen) {
win.setFullScreen(true);
} else if (loadedWindowState.isMaximized) {
win.maximize();
}
setMenu(win);
uhkHidDeviceService = new UhkHidDevice(logger, options, packagesDir);
uhkOperations = new UhkOperations(logger, uhkHidDeviceService, packagesDir);
deviceService = new DeviceService(logger, win, uhkHidDeviceService, uhkOperations, packagesDir);
appUpdateService = new AppUpdateService(logger, win, app);
appService = new AppService(logger, win, deviceService, options, uhkHidDeviceService);
sudoService = new SudoService(logger);
sudoService = new SudoService(logger, options);
// and load the index.html of the app.
win.loadURL(url.format({
@@ -104,13 +118,13 @@ function createWindow() {
});
// Emitted when the window is closed.
win.on('closed', () => {
win.on('closed', async () => {
// 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.
logger.info('[Electron Main] win closed');
win = null;
deviceService.close();
await deviceService.close();
deviceService = null;
appUpdateService = null;
appService = null;
@@ -125,31 +139,49 @@ function createWindow() {
win.webContents.on('crashed', (event: any) => {
logger.error(event);
});
win.on('close', () => saveWindowState(win));
win.on('resize', () => saveWindowState(win));
win.on('move', () => saveWindowState(win));
}
if (isSecondInstance) {
app.quit();
} else {
// 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);
app.on('ready', createWindow);
// Quit when all windows are closed.
app.on('window-all-closed', () => {
app.quit();
});
app.on('window-all-closed', () => {
if (appUpdateService) {
appUpdateService.saveFirtsRun();
}
app.exit();
});
app.on('will-quit', () => {
if (appUpdateService) {
appUpdateService.saveFirtsRun();
}
});
app.on('will-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();
}
});
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();
}
});
app.on('second-instance', () => {
// Someone tried to run a second instance, we should focus our window.
if (win) {
if (win.isMinimized()) {
win.restore();
}
win.focus();
}
});
}
// 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

View File

@@ -0,0 +1,37 @@
import { app, BrowserWindow, Menu, systemPreferences } from 'electron';
import * as isDev from 'electron-is-dev';
export const setMenu = (win: BrowserWindow): void => {
if (process.platform !== 'darwin' || isDev) {
win.setMenuBarVisibility(false);
return;
}
const template = [
{
label: app.getName(),
submenu: [
{role: 'quit'}
]
},
{
label: 'Edit',
submenu: [
{role: 'cut'},
{role: 'copy'},
{role: 'paste'},
{role: 'delete'},
{role: 'selectall'}
]
}
];
// hide "Start Dictation" submenu item in Edit menu
systemPreferences.setUserDefault('NSDisabledDictationMenuItem', 'boolean', true as any);
// hide "Emoji & Symbols" submenu item in Edit menu
systemPreferences.setUserDefault('NSDisabledCharacterPaletteMenuItem', 'boolean', false as any);
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
};

View File

@@ -1,4 +1,10 @@
export interface CommandLineInputs {
/**
* addons menu visible or not
*/
addons?: boolean;
'auto-write-config'?: boolean;
/**
* simulate privilege escalation error
*/
spe?: boolean;
}

View File

@@ -3,5 +3,6 @@ import { SynchrounousResult } from 'tmp';
export interface TmpFirmware {
rightFirmwarePath: string;
leftFirmwarePath: string;
packageJsonPath: string;
tmpDirectory: SynchrounousResult;
}

View File

@@ -0,0 +1,6 @@
import { Rectangle } from 'electron';
export interface WindowState extends Rectangle {
isMaximized: boolean;
isFullScreen: boolean;
}

View File

@@ -14,10 +14,6 @@
"npm": ">=5.1.0 <6.0.0"
},
"dependencies": {
"node-hid": "0.5.7"
},
"firmwareVersion": "8.0.0",
"deviceProtocolVersion": "4.0.0",
"userConfigVersion": "4.0.0",
"hardwareConfigVersion": "1.0.0"
"node-hid": "0.7.8"
}
}

View File

@@ -5,10 +5,13 @@ import * as settings from 'electron-settings';
import * as isDev from 'electron-is-dev';
import * as storage from 'electron-settings';
import { IpcEvents, LogService } from 'uhk-common';
import { AutoUpdateSettings, IpcEvents, LogService } from 'uhk-common';
import { MainServiceBase } from './main-service-base';
export class AppUpdateService extends MainServiceBase {
private sendAutoUpdateNotification = false;
constructor(protected logService: LogService,
protected win: Electron.BrowserWindow,
private app: Electron.App) {
@@ -24,16 +27,21 @@ export class AppUpdateService extends MainServiceBase {
private initListeners() {
autoUpdater.on('checking-for-update', () => {
this.logService.debug('[AppUpdateService] checking for update');
this.sendIpcToWindow(IpcEvents.autoUpdater.checkingForUpdate);
});
autoUpdater.on('update-available', async (ev: any, info: UpdateInfo) => {
this.logService.debug('[AppUpdateService] update available. Downloading started');
await autoUpdater.downloadUpdate();
this.sendIpcToWindow(IpcEvents.autoUpdater.updateAvailable, info);
});
autoUpdater.on('update-not-available', (ev: any, info: UpdateInfo) => {
this.sendIpcToWindow(IpcEvents.autoUpdater.updateNotAvailable, info);
if (this.sendAutoUpdateNotification) {
this.logService.debug('[AppUpdateService] update not available');
this.sendIpcToWindow(IpcEvents.autoUpdater.updateNotAvailable, info);
}
});
autoUpdater.on('error', (ev: any, err: string) => {
@@ -51,6 +59,7 @@ export class AppUpdateService extends MainServiceBase {
});
autoUpdater.on('update-downloaded', (ev: any, info: UpdateInfo) => {
this.logService.debug('[AppUpdateService] update downloaded');
this.sendIpcToWindow(IpcEvents.autoUpdater.autoUpdateDownloaded, info);
});
@@ -61,32 +70,46 @@ export class AppUpdateService extends MainServiceBase {
ipcMain.on(IpcEvents.app.appStarted, () => {
if (this.checkForUpdateAtStartup()) {
this.sendAutoUpdateNotification = false;
this.logService.debug('[AppUpdateService] app started. Automatically check for update.');
this.checkForUpdate();
}
});
ipcMain.on(IpcEvents.autoUpdater.checkForUpdate, () => {
this.logService.debug('[AppUpdateService] checkForUpdate request from renderer process');
this.checkForUpdate();
ipcMain.on(IpcEvents.autoUpdater.checkForUpdate, (event: Electron.Event, args: any[]) => {
const allowPrerelease: boolean = args[0];
// tslint:disable-next-line:max-line-length
const logMsg = `[AppUpdateService] checkForUpdate request from renderer process. Allow prerelease: ${allowPrerelease}`;
this.logService.debug(logMsg);
this.sendAutoUpdateNotification = true;
this.checkForUpdate(allowPrerelease);
});
}
private checkForUpdate() {
private checkForUpdate(allowPrerelease = false): void {
if (isDev) {
const msg = '[AppUpdateService] Application update is not working in dev mode.';
this.logService.info(msg);
this.sendIpcToWindow(IpcEvents.autoUpdater.checkForUpdateNotAvailable, msg);
if (this.sendAutoUpdateNotification) {
this.sendIpcToWindow(IpcEvents.autoUpdater.checkForUpdateNotAvailable, msg);
}
return;
}
if (this.isFirstRun()) {
const msg = '[AppUpdateService] Application update is skipping at first run.';
this.logService.info(msg);
this.sendIpcToWindow(IpcEvents.autoUpdater.checkForUpdateNotAvailable, msg);
if (this.sendAutoUpdateNotification) {
this.sendIpcToWindow(IpcEvents.autoUpdater.checkForUpdateNotAvailable, msg);
}
return;
}
autoUpdater.allowPrerelease = this.allowPreRelease();
autoUpdater.allowPrerelease = allowPrerelease;
autoUpdater.checkForUpdates()
.then(() => {
this.logService.debug('[AppUpdateService] checkForUpdate success');
@@ -107,12 +130,6 @@ export class AppUpdateService extends MainServiceBase {
return firstRunVersion !== this.app.getVersion();
}
private allowPreRelease() {
const autoUpdateSettings = this.getAutoUpdateSettings();
return autoUpdateSettings && autoUpdateSettings.usePreReleaseUpdate;
}
private checkForUpdateAtStartup() {
const autoUpdateSettings = this.getAutoUpdateSettings();
const checkForUpdate = autoUpdateSettings && autoUpdateSettings.checkForUpdateOnStartUp;
@@ -122,10 +139,10 @@ export class AppUpdateService extends MainServiceBase {
return checkForUpdate;
}
private getAutoUpdateSettings() {
private getAutoUpdateSettings(): AutoUpdateSettings {
const value = storage.get('auto-update-settings');
if (!value) {
return {checkForUpdateOnStartUp: false, usePreReleaseUpdate: false};
return {checkForUpdateOnStartUp: false};
}
return JSON.parse(<string>value);

View File

@@ -1,7 +1,6 @@
import { BrowserWindow, ipcMain } from 'electron';
import { ipcMain, shell } from 'electron';
import { UhkHidDevice } from 'uhk-usb';
import { readFile } from 'fs';
import { join } from 'path';
import * as os from 'os';
import { AppStartInfo, IpcEvents, LogService } from 'uhk-common';
import { MainServiceBase } from './main-service-base';
@@ -18,53 +17,31 @@ export class AppService extends MainServiceBase {
ipcMain.on(IpcEvents.app.getAppStartInfo, this.handleAppStartInfo.bind(this));
ipcMain.on(IpcEvents.app.exit, this.exit.bind(this));
ipcMain.on(IpcEvents.app.openUrl, this.openUrl.bind(this));
logService.info('[AppService] init success');
}
private async handleAppStartInfo(event: Electron.Event) {
this.logService.info('[AppService] getAppStartInfo');
const packageJson = await this.getPackageJson();
const deviceConnectionState = await this.uhkHidDeviceService.getDeviceConnectionStateAsync();
const response: AppStartInfo = {
deviceConnectionState,
commandLineArgs: {
addons: this.options.addons || false,
autoWriteConfig: this.options['auto-write-config'] || false
addons: this.options.addons || false
},
deviceConnected: this.uhkHidDeviceService.deviceConnected(),
hasPermission: this.uhkHidDeviceService.hasPermission(),
agentVersionInfo: {
version: packageJson.version,
firmwareVersion: packageJson.firmwareVersion,
deviceProtocolVersion: packageJson.deviceProtocolVersion,
moduleProtocolVersion: packageJson.moduleProtocolVersion,
userConfigVersion: packageJson.userConfigVersion,
hardwareConfigVersion: packageJson.hardwareConfigVersion
}
platform: process.platform as string,
osVersion: os.release()
};
this.logService.info('[AppService] getAppStartInfo response:', response);
return event.sender.send(IpcEvents.app.getAppStartInfoReply, response);
}
/**
* Read the package.json that delivered with the bundle. Do not use require('package.json')
* because the deploy process change the package.json after the build
* @returns {Promise<any>}
*/
private async getPackageJson(): Promise<any> {
return new Promise((resolve, reject) => {
readFile(join(__dirname, 'package.json'), {encoding: 'utf-8'}, (err, data) => {
if (err) {
return reject(err);
}
resolve(JSON.parse(data));
});
});
}
private exit() {
this.logService.info('[AppService] exit');
this.win.close();
}
private openUrl(event: Electron.Event, urls: Array<string>) {
shell.openExternal(urls[0]);
}
}

View File

@@ -1,19 +1,30 @@
import { ipcMain } from 'electron';
import { ConfigurationReply, DeviceConnectionState, IpcEvents, IpcResponse, LogService } from 'uhk-common';
import { isEqual } from 'lodash';
import {
ConfigurationReply,
DeviceConnectionState,
FirmwareUpgradeIpcResponse,
getHardwareConfigFromDeviceResponse,
HardwareModules,
IpcEvents,
IpcResponse,
LogService,
mapObjectToUserConfigBinaryBuffer,
SaveUserConfigurationData,
UpdateFirmwareData
} from 'uhk-common';
import { snooze, UhkHidDevice, UhkOperations } from 'uhk-usb';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import { emptyDir } from 'fs-extra';
import * as path from 'path';
import 'rxjs/add/observable/interval';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/distinctUntilChanged';
import { saveTmpFirmware } from '../util/save-extract-firmware';
import { TmpFirmware } from '../models/tmp-firmware';
import { QueueManager } from './queue-manager';
import {
backupUserConfiguration,
getBackupUserConfigurationContent,
getPackageJsonFromPathAsync,
saveTmpFirmware
} from '../util';
/**
* IpcMain pair of the UHK Communication
@@ -22,14 +33,20 @@ import { QueueManager } from './queue-manager';
* - Read UserConfiguration from the UHK Device
*/
export class DeviceService {
private pollTimer$: Subscription;
private _pollerAllowed: boolean;
private _uhkDevicePolling: boolean;
private queueManager = new QueueManager();
constructor(private logService: LogService,
private win: Electron.BrowserWindow,
private device: UhkHidDevice,
private operations: UhkOperations) {
this.pollUhkDevice();
private operations: UhkOperations,
private rootDir: string) {
this.startPollUhkDevice();
this.uhkDevicePoller()
.catch(error => {
this.logService.error('[DeviceService] UHK Device poller error', error);
});
ipcMain.on(IpcEvents.device.saveUserConfiguration, (...args: any[]) => {
this.queueManager.add({
@@ -58,7 +75,25 @@ export class DeviceService {
});
});
ipcMain.on(IpcEvents.device.startConnectionPoller, this.pollUhkDevice.bind(this));
ipcMain.on(IpcEvents.device.startConnectionPoller, this.startPollUhkDevice.bind(this));
ipcMain.on(IpcEvents.device.recoveryDevice, (...args: any[]) => {
this.queueManager.add({
method: this.recoveryDevice,
bind: this,
params: args,
asynchronous: true
});
});
ipcMain.on(IpcEvents.device.enableUsbStackTest, (...args: any[]) => {
this.queueManager.add({
method: this.enableUsbStackTest,
bind: this,
params: args,
asynchronous: true
});
});
logService.debug('[DeviceService] init success');
}
@@ -71,12 +106,20 @@ export class DeviceService {
let response: ConfigurationReply;
try {
await this.stopPollUhkDevice();
await this.device.waitUntilKeyboardBusy();
const result = await this.operations.loadConfigurations();
const modules: HardwareModules = await this.getHardwareModules(false);
const hardwareConfig = getHardwareConfigFromDeviceResponse(result.hardwareConfiguration);
const uniqueId = hardwareConfig.uniqueId;
response = {
success: true,
...result
...result,
modules,
backupConfiguration: await getBackupUserConfigurationContent(this.logService, uniqueId)
};
} catch (error) {
response = {
@@ -85,38 +128,78 @@ export class DeviceService {
};
} finally {
this.device.close();
this.startPollUhkDevice();
}
event.sender.send(IpcEvents.device.loadConfigurationReply, JSON.stringify(response));
}
public close(): void {
this.stopPollTimer();
public async getHardwareModules(catchError: boolean): Promise<HardwareModules> {
try {
await this.device.waitUntilKeyboardBusy();
return {
leftModuleInfo: await this.operations.getLeftModuleVersionInfo(),
rightModuleInfo: await this.operations.getRightModuleVersionInfo()
};
} catch (err) {
if (!catchError) {
return err;
}
this.logService.error('[DeviceService] Read hardware modules information failed', err);
return {
leftModuleInfo: {},
rightModuleInfo: {}
};
}
}
public async close(): Promise<void> {
await this.stopPollUhkDevice();
this.logService.info('[DeviceService] Device connection checker stopped.');
}
public async updateFirmware(event: Electron.Event, args?: Array<string>): Promise<void> {
const response = new IpcResponse();
const response = new FirmwareUpgradeIpcResponse();
const data: UpdateFirmwareData = JSON.parse(args[0]);
let firmwarePathData: TmpFirmware;
try {
this.stopPollTimer();
this.logService.debug('Agent version:', data.versionInformation.version);
const hardwareModules = await this.getHardwareModules(false);
this.logService.debug('Device right firmware version:', hardwareModules.rightModuleInfo.firmwareVersion);
this.logService.debug('Device left firmware version:', hardwareModules.leftModuleInfo.firmwareVersion);
await this.stopPollUhkDevice();
this.device.resetDeviceCache();
if (data.firmware) {
firmwarePathData = await saveTmpFirmware(data.firmware);
const packageJson = await getPackageJsonFromPathAsync(firmwarePathData.packageJsonPath);
this.logService.debug('New firmware version:', packageJson.firmwareVersion);
if (args && args.length > 0) {
firmwarePathData = await saveTmpFirmware(args[0]);
await this.operations.updateRightFirmware(firmwarePathData.rightFirmwarePath);
await this.operations.updateLeftModule(firmwarePathData.leftFirmwarePath);
}
else {
} else {
const packageJsonPath = path.join(this.rootDir, 'packages/firmware/package.json');
const packageJson = await getPackageJsonFromPathAsync(packageJsonPath);
this.logService.debug('New firmware version:', packageJson.firmwareVersion);
await this.operations.updateRightFirmware();
await this.operations.updateLeftModule();
}
response.success = true;
response.modules = await this.getHardwareModules(false);
} catch (error) {
const err = {message: error.message, stack: error.stack};
const err = { message: error.message, stack: error.stack };
this.logService.error('[DeviceService] updateFirmware error', err);
response.modules = await this.getHardwareModules(true);
response.error = err;
}
@@ -125,64 +208,107 @@ export class DeviceService {
}
await snooze(500);
this.startPollUhkDevice();
event.sender.send(IpcEvents.device.updateFirmwareReply, response);
}
public async recoveryDevice(event: Electron.Event): Promise<void> {
const response = new FirmwareUpgradeIpcResponse();
try {
await this.stopPollUhkDevice();
await this.operations.updateRightFirmware();
response.modules = await this.getHardwareModules(false);
response.success = true;
} catch (error) {
const err = { message: error.message, stack: error.stack };
this.logService.error('[DeviceService] updateFirmware error', err);
response.modules = await this.getHardwareModules(true);
response.error = err;
}
await snooze(500);
event.sender.send(IpcEvents.device.updateFirmwareReply, response);
}
public async enableUsbStackTest(event: Electron.Event) {
await this.device.enableUsbStackTest();
}
private startPollUhkDevice(): void {
this._pollerAllowed = true;
}
private async stopPollUhkDevice(): Promise<void> {
return new Promise<void>(async resolve => {
this._pollerAllowed = false;
while (true) {
if (!this._uhkDevicePolling) {
return resolve();
}
await snooze(100);
}
});
}
/**
* HID API not support device attached and detached event.
* This method check the keyboard is attached to the computer or not.
* Every second check the HID device list.
* The halves are connected and merged or not.
* Every 250ms check the HID device list.
* @private
*/
private pollUhkDevice(): void {
if (this.pollTimer$) {
return;
private async uhkDevicePoller(): Promise<void> {
let savedState: DeviceConnectionState;
while (true) {
if (this._pollerAllowed) {
this._uhkDevicePolling = true;
const state = await this.device.getDeviceConnectionStateAsync();
if (!isEqual(state, savedState)) {
savedState = state;
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, state);
this.logService.info('[DeviceService] Device connection state changed to:', state);
}
this._uhkDevicePolling = false;
}
await snooze(250);
}
this.pollTimer$ = Observable.interval(1000)
.startWith(0)
.map(() => this.device.deviceConnected())
.distinctUntilChanged()
.do((connected: boolean) => {
const response: DeviceConnectionState = {
connected,
hasPermission: this.device.hasPermission()
};
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, response);
this.logService.info('[DeviceService] Device connection state changed to:', response);
})
.subscribe();
}
private async saveUserConfiguration(event: Electron.Event, args: Array<string>): Promise<void> {
const response = new IpcResponse();
const json = args[0];
const data: SaveUserConfigurationData = JSON.parse(args[0]);
try {
await this.operations.saveUserConfiguration(json);
await this.stopPollUhkDevice();
await backupUserConfiguration(data);
const buffer = mapObjectToUserConfigBinaryBuffer(data.configuration);
await this.operations.saveUserConfiguration(buffer);
response.success = true;
}
catch (error) {
} catch (error) {
this.logService.error('[DeviceService] Transferring error', error);
response.error = {message: error.message};
response.error = { message: error.message };
} finally {
this.device.close();
this.startPollUhkDevice();
}
event.sender.send(IpcEvents.device.saveUserConfigurationReply, response);
return Promise.resolve();
}
private stopPollTimer(): void {
if (!this.pollTimer$) {
return;
}
this.pollTimer$.unsubscribe();
this.pollTimer$ = null;
}
}

View File

@@ -5,12 +5,13 @@ import * as sudo from 'sudo-prompt';
import { dirSync } from 'tmp';
import { emptyDir, copy } from 'fs-extra';
import { IpcEvents, LogService, IpcResponse } from 'uhk-common';
import { CommandLineArgs, IpcEvents, LogService, IpcResponse } from 'uhk-common';
export class SudoService {
private rootDir: string;
constructor(private logService: LogService) {
constructor(private logService: LogService,
private options: CommandLineArgs) {
if (isDev) {
this.rootDir = path.join(path.join(process.cwd(), process.argv[1]), '../../../../');
} else {
@@ -21,6 +22,19 @@ export class SudoService {
}
private async setPrivilege(event: Electron.Event) {
if (this.options.spe) {
const error = new Error('No polkit authentication agent found.');
this.logService.error('[SudoService] Simulate privilege escalation error ', error);
const response = new IpcResponse();
response.success = false;
response.error = {message: error.message};
event.sender.send(IpcEvents.device.setPrivilegeOnLinuxReply, response);
return;
}
switch (process.platform) {
case 'linux':
await this.setPrivilegeOnLinux(event);
@@ -28,7 +42,7 @@ export class SudoService {
default:
const response: IpcResponse = {
success: false,
error: { message: 'Permissions couldn\'t be set. Invalid platform: ' + process.platform }
error: {message: 'Permissions couldn\'t be set. Invalid platform: ' + process.platform}
};
event.sender.send(IpcEvents.device.setPrivilegeOnLinuxReply, response);
@@ -39,7 +53,7 @@ export class SudoService {
private async setPrivilegeOnLinux(event: Electron.Event) {
const tmpDirectory = dirSync();
const rulesDir = path.join(this.rootDir, 'rules');
this.logService.debug('[SudoService] Copy rules dir', { src: rulesDir, dst: tmpDirectory.name });
this.logService.debug('[SudoService] Copy rules dir', {src: rulesDir, dst: tmpDirectory.name});
await copy(rulesDir, tmpDirectory.name);
const scriptPath = path.join(tmpDirectory.name, 'setup-rules.sh');
@@ -55,7 +69,7 @@ export class SudoService {
if (error) {
this.logService.error('[SudoService] Error when set privilege: ', error);
response.success = false;
response.error = error;
response.error = {message: error.message};
} else {
response.success = true;
}

View File

@@ -0,0 +1,32 @@
import { app } from 'electron';
import { LogService, UserConfiguration, SaveUserConfigurationData } from 'uhk-common';
import * as path from 'path';
import * as fs from 'fs-extra';
export const getBackupUserConfigurationPath = (uniqueId: number): string => {
const appDataDir = app.getPath('userData');
return path.join(appDataDir, `${uniqueId}.json`);
};
export const backupUserConfiguration = (data: SaveUserConfigurationData): Promise<void> => {
const backupFilePath = getBackupUserConfigurationPath(data.uniqueId);
return fs.writeJSON(backupFilePath, data.configuration, {spaces: 2});
};
export const getBackupUserConfigurationContent = async (logService: LogService, uniqueId: number): Promise<UserConfiguration> => {
try {
const backupFilePath = getBackupUserConfigurationPath(uniqueId);
if (await fs.pathExists(backupFilePath)) {
const json = await fs.readJSON(backupFilePath);
new UserConfiguration().fromJsonObject(json);
return json;
}
return null;
} catch (error) {
logService.error('Can not load backup user configuration for device', {uniqueId, error});
}
};

View File

@@ -0,0 +1,13 @@
import * as fs from 'fs';
export const getPackageJsonFromPathAsync = async (filePath: string): Promise<any> => {
return new Promise((resolve, reject) => {
fs.readFile(filePath, {encoding: 'utf-8'}, (err, data) => {
if (err) {
return reject(err);
}
resolve(JSON.parse(data));
});
});
};

View File

@@ -0,0 +1,3 @@
export * from './backup-user-confoguration';
export * from './get-package-json-from-path-async';
export * from './save-extract-firmware';

View File

@@ -6,7 +6,7 @@ import * as decompressTarbz from 'decompress-tarbz2';
import { TmpFirmware } from '../models/tmp-firmware';
export async function saveTmpFirmware(data: string): Promise<TmpFirmware> {
export async function saveTmpFirmware(data: Array<number>): Promise<TmpFirmware> {
const tmpDirectory = dirSync();
const zipFilePath = path.join(tmpDirectory.name, 'firmware.bz2');
@@ -16,15 +16,14 @@ export async function saveTmpFirmware(data: string): Promise<TmpFirmware> {
return {
tmpDirectory,
rightFirmwarePath: path.join(tmpDirectory.name, 'devices/uhk60-right/firmware.hex'),
leftFirmwarePath: path.join(tmpDirectory.name, 'modules/uhk60-left.bin')
leftFirmwarePath: path.join(tmpDirectory.name, 'modules/uhk60-left.bin'),
packageJsonPath: path.join(tmpDirectory.name, 'package.json')
};
}
function writeDataToFile(data: string, filePath: string): Promise<void> {
function writeDataToFile(data: Array<number>, filePath: string): Promise<void> {
return new Promise((resolve, reject) => {
const array: Array<number> = JSON.parse(data);
const buffer = new Buffer(array);
const buffer = Buffer.from(data);
fs.writeFile(filePath, buffer, err => {
if (err) {

View File

@@ -0,0 +1,70 @@
import * as electron from 'electron';
import * as settings from 'electron-settings';
import { logger } from '../services/logger.service';
import { WindowState } from '../models/window-state';
const WINDOWS_SETTINGS_KEY = 'windowSettings';
export const windowVisibleFilter = (state: WindowState) => {
return (display: electron.Display): boolean => (
state.x >= display.bounds.x &&
state.y >= display.bounds.y &&
state.x <= display.bounds.width &&
state.y <= display.bounds.height
);
};
export const windowVisibleOnScreen = (state: WindowState): boolean => {
return electron.screen.getAllDisplays().some(windowVisibleFilter(state));
};
export const getDefaultWindowState = () => ({
width: 1024,
height: 768,
isMaximized: true
});
export const loadWindowState = (): Partial<WindowState> => {
logger.log('[WindowState] load settings');
try {
const loadedState = settings.get(WINDOWS_SETTINGS_KEY) as any;
logger.log('[WindowState] loaded settings', loadedState);
if (!loadedState) {
logger.log('[WindowState]save state not exists, use default');
return getDefaultWindowState();
}
const visible = windowVisibleOnScreen(loadedState);
logger.log('[WindowState] loaded settings is visible', visible);
if (visible) {
logger.log('[WindowState] return with loaded settings');
return loadedState;
}
} catch (err) {
logger.error('[WindowState] error when parsing loaded settings', err);
}
logger.log('[WindowState] return with default settings');
return getDefaultWindowState();
};
export const saveWindowState = (win: electron.BrowserWindow) => {
const winBounds = win.isMaximized() || win.isFullScreen()
? loadWindowState() as any
: win.getBounds();
const state: WindowState = {
...winBounds,
isMaximized: win.isMaximized(),
isFullScreen: win.isFullScreen()
};
logger.log('[WindowState] save settings:', state);
settings.set(WINDOWS_SETTINGS_KEY, state as any);
logger.log('[WindowState] save settings success');
};

View File

@@ -1,21 +1,7 @@
{
"compileOnSave": false,
"extends": "../../tsconfig.json",
"compilerOptions": {
"sourceMap": true,
"declaration": false,
"module": "commonjs",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es2016",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2015.iterable",
"es2016",
"dom",
"dom.iterable"
]
"baseUrl": "./src",
"declaration": false
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,28 +3,27 @@
"private": true,
"version": "1.0.0",
"description": "Common Library contains the common code for uhk-agent (electron-main) and web (electron-renderer) modules",
"main": "dist/index.js",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
"author": "Ultimate Gadget Laboratories",
"repository": {
"type": "git",
"url": "git@github.com:UltimateHackingKeyboard/agent.git"
},
"scripts": {
"build": "tsc",
"build": "run-s tsc copy:*",
"tsc": "tsc",
"copy:scancodes": "copyfiles ./src/config-serializer/config-items/scancodes.json dist",
"copy:secondary-roles": "copyfiles ./src/config-serializer/config-items/secondaryRole.json dist",
"test": "jasmine-ts --config=jasmine.json",
"coverage": "nyc jasmine-ts --config=jasmine.json"
"coverage": "nyc jasmine-ts --config=jasmine.json",
"lint": "tslint --project tsconfig.json"
},
"dependencies": {
"buffer": "5.2.1",
"tslib": "1.10.0"
},
"license": "GPL-3.0",
"devDependencies": {
"@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",
"nyc": "11.2.1",
"ts-node": "3.3.0"
},
"nyc": {
"extension": [
".ts"

View File

@@ -41,16 +41,20 @@ export class HardwareConfiguration {
}
fromBinary(buffer: UhkBuffer): HardwareConfiguration {
this.signature = buffer.readString();
this.majorVersion = buffer.readUInt8();
this.minorVersion = buffer.readUInt8();
this.patchVersion = buffer.readUInt8();
this.brandId = buffer.readUInt8();
this.deviceId = buffer.readUInt8();
this.uniqueId = buffer.readUInt32();
this.isVendorModeOn = buffer.readBoolean();
this.isIso = buffer.readBoolean();
return this;
try {
this.signature = buffer.readString();
this.majorVersion = buffer.readUInt8();
this.minorVersion = buffer.readUInt8();
this.patchVersion = buffer.readUInt8();
this.brandId = buffer.readUInt8();
this.deviceId = buffer.readUInt8();
this.uniqueId = buffer.readUInt32();
this.isVendorModeOn = buffer.readBoolean();
this.isIso = buffer.readBoolean();
return this;
} catch (e) {
throw new Error('Please power cycle your keyboard (Invalid hardware configuration: Index out of bounds)');
}
}
toJsonObject(): any {

View File

@@ -8,4 +8,8 @@ export * from './secondary-role-action';
export * from './macro';
export * from './module';
export * from './module-configuration';
export * from './mouse-speed-configuration';
export * from './user-configuration';
export const SCANCODES = require('./scancodes.json');
export const SECONDARY_ROLES = require('./secondaryRole.json');

View File

@@ -7,6 +7,8 @@ import { SwitchLayerAction } from './switch-layer-action';
import { SwitchKeymapAction, UnresolvedSwitchKeymapAction } from './switch-keymap-action';
import { MouseAction } from './mouse-action';
import { PlayMacroAction } from './play-macro-action';
import { NoneAction } from './none-action';
import { isScancodeExists } from '../scancode-checker';
export class Helper {
@@ -25,7 +27,12 @@ export class Helper {
buffer.backtrack();
if (keyActionFirstByte >= KeyActionId.KeystrokeAction && keyActionFirstByte < KeyActionId.LastKeystrokeAction) {
return new KeystrokeAction().fromBinary(buffer);
const keystrokeAction = new KeystrokeAction().fromBinary(buffer);
if (isValidKeystrokeAction(keystrokeAction)) {
return keystrokeAction;
}
return new NoneAction();
}
switch (keyActionFirstByte) {
@@ -67,8 +74,14 @@ export class Helper {
}
switch (keyAction.keyActionType) {
case keyActionType.KeystrokeAction:
return new KeystrokeAction().fromJsonObject(keyAction);
case keyActionType.KeystrokeAction: {
const keystrokeAction = new KeystrokeAction().fromJsonObject(keyAction);
if (isValidKeystrokeAction(keystrokeAction)) {
return keystrokeAction;
}
return new NoneAction();
}
case keyActionType.SwitchLayerAction:
return new SwitchLayerAction().fromJsonObject(keyAction);
case keyActionType.SwitchKeymapAction:
@@ -77,8 +90,16 @@ export class Helper {
return new MouseAction().fromJsonObject(keyAction);
case keyActionType.PlayMacroAction:
return new PlayMacroAction().fromJsonObject(keyAction, macros);
case keyActionType.NoneAction:
return new NoneAction();
default:
throw `Invalid KeyAction.keyActionType: "${keyAction.keyActionType}"`;
}
}
}
function isValidKeystrokeAction(keystrokeAction: KeystrokeAction): boolean {
return keystrokeAction.hasSecondaryRoleAction() ||
keystrokeAction.hasActiveModifier() ||
keystrokeAction.hasScancode() && isScancodeExists(keystrokeAction.scancode);
}

View File

@@ -52,7 +52,7 @@ describe('keystroke-action', () => {
it('should not change the "type" to "shortMedia" when is "longMedia" if scancode < 256', () => {
const action = new KeystrokeAction();
action.type = KeystrokeType.longMedia;
action.type = KeystrokeType.longMedia as any;
action.scancode = 125;
expect(action.type).toEqual(KeystrokeType.shortMedia);
});
@@ -67,7 +67,7 @@ describe('keystroke-action', () => {
it('should not change the "type" to "longMedia" when is "shortMedia" if scancode >= 256', () => {
const action = new KeystrokeAction();
action.type = KeystrokeType.shortMedia;
action.type = KeystrokeType.shortMedia as any;
action.scancode = 256;
expect(action.type).toEqual(KeystrokeType.longMedia);
});
@@ -145,7 +145,7 @@ describe('keystroke-action', () => {
});
it('should change the value to "longMedia" if scancode >= 256 and value "shortMedia"', () => {
const value = KeystrokeType.shortMedia;
const value = KeystrokeType.shortMedia as any;
const scancode = 256;
const action = new KeystrokeAction();
action.scancode = scancode;
@@ -165,7 +165,7 @@ describe('keystroke-action', () => {
});
it('should change the value to "shortMedia" if scancode < 256 and value "longMedia"', () => {
const value = KeystrokeType.longMedia;
const value = KeystrokeType.longMedia as any;
const scancode = 100;
const action = new KeystrokeAction();
action.scancode = scancode;

View File

@@ -13,7 +13,7 @@ export enum KeystrokeActionFlag {
const KEYSTROKE_ACTION_FLAG_LENGTH = 3;
interface JsonObjectKeystrokeAction {
export interface JsonObjectKeystrokeAction {
keyActionType: string;
scancode?: number;
modifierMask?: number;

View File

@@ -1,17 +1,18 @@
import { binaryDefaultHelper, jsonDefaultHelper } from '../../../../test/serializer-test-helper';
import { SwitchLayerAction } from './switch-layer-action';
import { SwitchLayerAction, SwitchLayerMode } from './switch-layer-action';
import { keyActionType } from './key-action';
// TODO: Add null, undefined, empty object, empty buffer test cases
describe('switch-layer-action', () => {
const action = new SwitchLayerAction(<SwitchLayerAction>{layer: 0, isLayerToggleable: false});
const action = new SwitchLayerAction(<SwitchLayerAction>{layer: 0, switchLayerMode: SwitchLayerMode.hold});
it('should be instantiate', () => {
expect(new SwitchLayerAction()).toBeTruthy();
});
describe('toString', () => {
it('should return <SwitchLayerAction layer="0" toggle="false">', () => {
expect(action.toString()).toEqual('<SwitchLayerAction layer="0" toggle="false">');
it('should return <SwitchLayerAction layer="0" switchLayerMode="hold">', () => {
expect(action.toString()).toEqual('<SwitchLayerAction layer="0" switchLayerMode="hold">');
});
});
@@ -30,4 +31,20 @@ describe('switch-layer-action', () => {
binaryDefaultHelper(action);
});
});
describe('backward compatibility of the "toggle" property ', () => {
it('should map toggle=false to SwitchLayerMode.holdAndDoubleTapToggle', () => {
const oldAction = new SwitchLayerAction();
oldAction.fromJsonObject({keyActionType: keyActionType.SwitchLayerAction, layer: 0, toggle: false});
expect(oldAction.switchLayerMode).toEqual(SwitchLayerMode.holdAndDoubleTapToggle);
});
it('should map toggle=true to SwitchLayerMode.toggle', () => {
const oldAction = new SwitchLayerAction();
oldAction.fromJsonObject({keyActionType: keyActionType.SwitchLayerAction, layer: 0, toggle: true});
expect(oldAction.switchLayerMode).toEqual(SwitchLayerMode.toggle);
});
});
});

View File

@@ -8,9 +8,48 @@ export enum LayerName {
mouse
}
export enum SwitchLayerMode {
holdAndDoubleTapToggle = 'holdAndDoubleTapToggle',
toggle = 'toggle',
hold = 'hold'
}
export const mapSwitchLayerModeToNumber = (switchLayerMode: SwitchLayerMode): number => {
switch (switchLayerMode) {
case SwitchLayerMode.holdAndDoubleTapToggle:
return 0;
case SwitchLayerMode.toggle:
return 1;
case SwitchLayerMode.hold:
return 2;
default:
throw new Error(`Can not map ${switchLayerMode} to number`);
}
};
export const mapNumberToSwitchLayerMode = (value: number): SwitchLayerMode => {
switch (value) {
case 0:
return SwitchLayerMode.holdAndDoubleTapToggle;
case 1:
return SwitchLayerMode.toggle;
case 2:
return SwitchLayerMode.hold;
default:
throw new Error(`Can not map "${value}" to SwitchLayerMode`);
}
};
export class SwitchLayerAction extends KeyAction {
isLayerToggleable: boolean;
@assertEnum(SwitchLayerMode)
switchLayerMode: SwitchLayerMode;
@assertEnum(LayerName)
layer: LayerName;
@@ -20,21 +59,29 @@ export class SwitchLayerAction extends KeyAction {
if (!other) {
return;
}
this.isLayerToggleable = other.isLayerToggleable;
this.switchLayerMode = other.switchLayerMode;
this.layer = other.layer;
}
fromJsonObject(jsonObject: any): SwitchLayerAction {
this.assertKeyActionType(jsonObject);
this.layer = LayerName[<string>jsonObject.layer];
this.isLayerToggleable = jsonObject.toggle;
// Backward compatibility when "switchLayerMode" was a boolean type as "toggle"
if (typeof jsonObject.toggle === 'boolean') {
this.switchLayerMode = jsonObject.toggle ? SwitchLayerMode.toggle : SwitchLayerMode.holdAndDoubleTapToggle;
}
else {
this.switchLayerMode = jsonObject.switchLayerMode;
}
return this;
}
fromBinary(buffer: UhkBuffer): SwitchLayerAction {
this.readAndAssertKeyActionId(buffer);
this.layer = buffer.readUInt8();
this.isLayerToggleable = buffer.readBoolean();
this.switchLayerMode = mapNumberToSwitchLayerMode(buffer.readUInt8());
return this;
}
@@ -42,18 +89,18 @@ export class SwitchLayerAction extends KeyAction {
return {
keyActionType: keyActionType.SwitchLayerAction,
layer: LayerName[this.layer],
toggle: this.isLayerToggleable
switchLayerMode: this.switchLayerMode
};
}
toBinary(buffer: UhkBuffer) {
buffer.writeUInt8(KeyActionId.SwitchLayerAction);
buffer.writeUInt8(this.layer);
buffer.writeBoolean(this.isLayerToggleable);
buffer.writeUInt8(mapSwitchLayerModeToNumber(this.switchLayerMode));
}
toString(): string {
return `<SwitchLayerAction layer="${this.layer}" toggle="${this.isLayerToggleable}">`;
return `<SwitchLayerAction layer="${this.layer}" switchLayerMode="${this.switchLayerMode}">`;
}
public getName(): string {

View File

@@ -127,7 +127,7 @@ export class Keymap {
if (currentLayerId - 1 === baseKeyAction.layer) {
if (currentKeyAction instanceof SwitchLayerAction) {
if (currentKeyAction.layer === baseKeyAction.layer &&
currentKeyAction.isLayerToggleable === baseKeyAction.isLayerToggleable) {
currentKeyAction.switchLayerMode === baseKeyAction.switchLayerMode) {
continue;
}
// tslint:disable-next-line: max-line-length

View File

@@ -1,10 +1,10 @@
import { assertEnum, assertUInt8 } from '../../assert';
import { assertEnum, assertUInt8, assertUInt16 } from '../../assert';
import { UhkBuffer } from '../../uhk-buffer';
import { KeyModifiers } from '../key-modifiers';
import { MacroAction, MacroActionId, MacroKeySubAction, macroActionType } from './macro-action';
import { KeystrokeType } from '../key-action';
interface JsObjectKeyMacroAction {
export interface JsObjectKeyMacroAction {
macroActionType: string;
action: string;
type?: string;
@@ -20,12 +20,24 @@ export class KeyMacroAction extends MacroAction {
@assertEnum(KeystrokeType)
type: KeystrokeType;
@assertUInt8
scancode: number;
@assertUInt8
modifierMask: number;
@assertUInt16
private _scancode: number;
set scancode(scancode: number) {
this._scancode = scancode;
if (this.type !== KeystrokeType.shortMedia && this.type !== KeystrokeType.longMedia) {
return;
}
this.type = scancode < 256 ? KeystrokeType.shortMedia : KeystrokeType.longMedia;
}
get scancode() {
return this._scancode;
}
constructor(other?: KeyMacroAction) {
super();
if (!other) {
@@ -33,7 +45,7 @@ export class KeyMacroAction extends MacroAction {
}
this.action = other.action;
this.type = other.type;
this.scancode = other.scancode;
this._scancode = other._scancode;
this.modifierMask = other.modifierMask;
}
@@ -45,7 +57,7 @@ export class KeyMacroAction extends MacroAction {
} else {
this.type = KeystrokeType[jsObject.type];
}
this.scancode = jsObject.scancode;
this._scancode = jsObject.scancode;
this.modifierMask = jsObject.modifierMask;
return this;
}
@@ -58,7 +70,7 @@ export class KeyMacroAction extends MacroAction {
this.type = keyMacroType & 0b11;
keyMacroType >>= 2;
if (keyMacroType & 0b10) {
this.scancode = buffer.readUInt8();
this._scancode = this.type === KeystrokeType.longMedia ? buffer.readUInt16() : buffer.readUInt8();
}
if (keyMacroType & 0b01) {
this.modifierMask = buffer.readUInt8();
@@ -78,7 +90,7 @@ export class KeyMacroAction extends MacroAction {
} else {
jsObject.type = KeystrokeType[this.type];
}
jsObject.scancode = this.scancode;
jsObject.scancode = this._scancode;
}
if (this.hasModifiers()) {
@@ -98,7 +110,11 @@ export class KeyMacroAction extends MacroAction {
buffer.writeUInt8(keyMacroType);
if (this.hasScancode()) {
buffer.writeUInt8(this.scancode);
if (this.type === KeystrokeType.longMedia) {
buffer.writeUInt16(this.scancode);
} else {
buffer.writeUInt8(this.scancode);
}
}
if (this.hasModifiers()) {
buffer.writeUInt8(this.modifierMask);
@@ -106,7 +122,7 @@ export class KeyMacroAction extends MacroAction {
}
toString(): string {
return `<KeyMacroAction action="${this.action}" scancode="${this.scancode}" modifierMask="${this.modifierMask}">`;
return `<KeyMacroAction action="${this.action}" scancode="${this._scancode}" modifierMask="${this.modifierMask}">`;
}
isModifierActive(modifier: KeyModifiers): boolean {
@@ -114,7 +130,7 @@ export class KeyMacroAction extends MacroAction {
}
hasScancode(): boolean {
return !!this.scancode;
return !!this._scancode;
}
hasModifiers(): boolean {

View File

@@ -109,7 +109,7 @@ describe('keymap', () => {
{
keyActionType: 'switchLayer',
layer: 'mod',
toggle: false
switchLayerMode: 'holdAndDoubleTapToggle'
}
]
}]
@@ -121,7 +121,7 @@ describe('keymap', () => {
{
keyActionType: 'switchLayer',
layer: 'mod',
toggle: false
switchLayerMode: 'holdAndDoubleTapToggle'
}
]
}]
@@ -151,7 +151,7 @@ describe('keymap', () => {
expect(inputUserConfig.toJsonObject()).toEqual(expectedJsonConfig);
// tslint:disable-next-line: max-line-length
expect(console.warn).toHaveBeenCalledWith('QWERTY.layers[1]modules[0].keyActions[0] is not switch layer. <KeystrokeAction type="basic" scancode="44"> will be override with <SwitchLayerAction layer="0" toggle="false">');
expect(console.warn).toHaveBeenCalledWith('QWERTY.layers[1]modules[0].keyActions[0] is not switch layer. <KeystrokeAction type="basic" scancode="44"> will be override with <SwitchLayerAction layer="0" switchLayerMode="holdAndDoubleTapToggle">');
});
it('should normalize SwitchLayerAction if non base layer action is other SwitchLayerAction', () => {
@@ -262,7 +262,7 @@ describe('keymap', () => {
{
keyActionType: 'switchLayer',
layer: 'mod',
toggle: false
switchLayerMode: 'holdAndDoubleTapToggle'
}
]
}]
@@ -274,7 +274,7 @@ describe('keymap', () => {
{
keyActionType: 'switchLayer',
layer: 'mod',
toggle: false
switchLayerMode: 'holdAndDoubleTapToggle'
}
]
}]
@@ -304,6 +304,6 @@ describe('keymap', () => {
expect(inputUserConfig.toJsonObject()).toEqual(expectedJsonConfig);
// tslint:disable-next-line: max-line-length
expect(console.warn).toHaveBeenCalledWith('QWERTY.layers[1]modules[0].keyActions[0] is different switch layer. <SwitchLayerAction layer="1" toggle="false"> will be override with <SwitchLayerAction layer="0" toggle="false">');
expect(console.warn).toHaveBeenCalledWith('QWERTY.layers[1]modules[0].keyActions[0] is different switch layer. <SwitchLayerAction layer="1" switchLayerMode="holdAndDoubleTapToggle"> will be override with <SwitchLayerAction layer="0" switchLayerMode="holdAndDoubleTapToggle">');
});
});

View File

@@ -3,12 +3,12 @@ import { UhkBuffer } from '../../uhk-buffer';
import { MacroAction, MacroActionId, MacroMouseSubAction, macroActionType } from './macro-action';
export enum MouseButtons {
Left = 1 << 0,
Middle = 1 << 1,
Right = 1 << 2
Left = 0,
Right = 1,
Middle = 2
}
interface JsObjectMouseButtonMacroAction {
export interface JsObjectMouseButtonMacroAction {
macroActionType: string;
action: string;
mouseButtonsMask?: number;

View File

@@ -54,15 +54,12 @@ export class Module {
const noneAction = new NoneAction();
const keyActions: KeyAction[] = this.keyActions.map(keyAction => {
buffer.writeArray(this.keyActions, (uhkBuffer: UhkBuffer, keyAction: KeyAction) => {
if (keyAction) {
return keyAction;
keyAction.toBinary(uhkBuffer, userConfiguration);
} else {
noneAction.toBinary(uhkBuffer);
}
return noneAction;
});
buffer.writeArray(keyActions, (uhkBuffer: UhkBuffer, keyAction: KeyAction) => {
keyAction.toBinary(uhkBuffer, userConfiguration);
});
}

View File

@@ -0,0 +1,12 @@
export interface MouseSpeedConfiguration {
mouseMoveInitialSpeed: number;
mouseMoveAcceleration: number;
mouseMoveDeceleratedSpeed: number;
mouseMoveBaseSpeed: number;
mouseMoveAcceleratedSpeed: number;
mouseScrollInitialSpeed: number;
mouseScrollAcceleration: number;
mouseScrollDeceleratedSpeed: number;
mouseScrollBaseSpeed: number;
mouseScrollAcceleratedSpeed: number;
}

View File

@@ -0,0 +1,26 @@
import { SCANCODES } from './';
let scancodeMap: Map<number, any>;
export function isScancodeExists(scancode: number): boolean {
if (!scancodeMap) {
fillScancodeMap();
}
return scancodeMap.has(scancode);
}
function fillScancodeMap(): void {
scancodeMap = new Map<number, any>();
for (const scanGroup of SCANCODES) {
for (const child of scanGroup.children) {
if (child.additional && child.additional.scancode) {
scancodeMap.set(child.additional.scancode, child);
}
else {
scancodeMap.set(Number.parseInt(child.id), child);
}
}
}
}

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