Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c7d625573 | ||
|
|
52a57c0e87 | ||
|
|
a52b34fc3f | ||
|
|
1a14ac020e | ||
|
|
330f7e72be | ||
|
|
1ed6669ced | ||
|
|
108d60a497 | ||
|
|
1a9bd7de83 | ||
|
|
10cd06c70b | ||
|
|
84b6c33c54 | ||
|
|
18808eae9c | ||
|
|
ca74b0a76b | ||
|
|
b45d60efb2 | ||
|
|
a4e3696078 | ||
|
|
2b963993d2 | ||
|
|
e333022043 | ||
|
|
2e2a59ccb8 | ||
|
|
6f073ad718 | ||
|
|
166834e46c | ||
|
|
0ff2364b9e | ||
|
|
2cbfc6a11e | ||
|
|
404ccc7b2b | ||
|
|
42e413ab65 | ||
|
|
d57ef66038 | ||
|
|
843b4cbf68 | ||
|
|
7e4b7c5c8b | ||
|
|
2ff65537a0 | ||
|
|
6e2b1fb18d |
20
CHANGELOG.md
@@ -6,6 +6,26 @@ The format is loosely based on [Keep a Changelog](http://keepachangelog.com/en/1
|
||||
|
||||
Every Agent version includes the most recent firmware version. See the [firmware changelog](https://github.com/UltimateHackingKeyboard/firmware/blob/master/CHANGELOG.md).
|
||||
|
||||
## [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
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
If you're using Karabiner Elements on your Mac, then stop here! Make sure to close Karabiner Elements, then try to reproduce the issue again, even if you think that Karabiner Elements shouldn't be the cause. Karabiner Elements is the source of numerous problems, and we don't want to receive any more reports it causes.
|
||||
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.
|
||||
|
||||
4424
package-lock.json
generated
17
package.json
@@ -3,8 +3,8 @@
|
||||
"private": true,
|
||||
"author": "Ultimate Gadget Laboratories",
|
||||
"main": "electron/dist/electron-main.js",
|
||||
"version": "1.2.11",
|
||||
"firmwareVersion": "8.2.5",
|
||||
"version": "1.2.12",
|
||||
"firmwareVersion": "8.5.3",
|
||||
"deviceProtocolVersion": "4.4.0",
|
||||
"userConfigVersion": "4.0.1",
|
||||
"hardwareConfigVersion": "1.0.0",
|
||||
@@ -27,8 +27,9 @@
|
||||
"@types/jsonfile": "4.0.1",
|
||||
"@types/lodash-es": "4.17.0",
|
||||
"@types/node": "8.0.53",
|
||||
"@types/node-hid": "0.5.2",
|
||||
"@types/node-hid": "0.7.0",
|
||||
"@types/request": "2.0.8",
|
||||
"@types/semver": "5.5.0",
|
||||
"@types/usb": "1.1.3",
|
||||
"autoprefixer": "6.5.3",
|
||||
"buffer": "5.0.6",
|
||||
@@ -41,7 +42,7 @@
|
||||
"decompress-tarbz2": "4.1.1",
|
||||
"devtron": "1.4.0",
|
||||
"electron": "1.8.7",
|
||||
"electron-builder": "20.8.1",
|
||||
"electron-builder": "20.34.0",
|
||||
"electron-debug": "1.5.0",
|
||||
"electron-devtools-installer": "2.2.3",
|
||||
"electron-log": "2.2.16",
|
||||
@@ -57,16 +58,16 @@
|
||||
"jasmine-node": "2.0.1",
|
||||
"jasmine-ts": "0.2.1",
|
||||
"jsonfile": "4.0.0",
|
||||
"lerna": "3.1.4",
|
||||
"lerna": "3.2.0",
|
||||
"lodash-es": "4.17.4",
|
||||
"mkdirp": "0.5.1",
|
||||
"node-hid": "0.5.7",
|
||||
"node-hid": "0.7.3",
|
||||
"npm-run-all": "4.0.2",
|
||||
"pre-commit": "1.2.2",
|
||||
"request": "2.88.0",
|
||||
"rimraf": "2.6.1",
|
||||
"standard-version": "4.2.0",
|
||||
"stylelint": "9.5.0",
|
||||
"standard-version": "4.4.0",
|
||||
"stylelint": "9.6.0",
|
||||
"svg-sprite": "1.5.0",
|
||||
"ts-loader": "2.3.1",
|
||||
"ts-node": "7.0.1",
|
||||
|
||||
100
packages/uhk-agent/package-lock.json
generated
@@ -36,9 +36,9 @@
|
||||
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
|
||||
"integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
|
||||
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
|
||||
"requires": {
|
||||
"delegates": "^1.0.0",
|
||||
"readable-stream": "^2.0.6"
|
||||
@@ -87,9 +87,9 @@
|
||||
"integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
|
||||
},
|
||||
"chownr": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz",
|
||||
"integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE="
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
|
||||
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g=="
|
||||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
@@ -221,9 +221,9 @@
|
||||
}
|
||||
},
|
||||
"deep-extend": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz",
|
||||
"integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8="
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
|
||||
},
|
||||
"delegates": {
|
||||
"version": "1.0.0",
|
||||
@@ -244,9 +244,9 @@
|
||||
}
|
||||
},
|
||||
"expand-template": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.0.tgz",
|
||||
"integrity": "sha512-kkjwkMqj0h4w/sb32ERCDxCQkREMCAgS39DscDnSwDsbxnwwM1BTZySdC3Bn1lhY7vL08n9GoO/fVTynjDgRyQ=="
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.1.tgz",
|
||||
"integrity": "sha512-cebqLtV8KOZfw0UI8TEFWxtczxxC1jvyUvx6H4fyp1K1FN7A4Q+uggVUlOsI1K8AGU0rwOGqP8nCapdrw8CYQg=="
|
||||
},
|
||||
"fd-slicer": {
|
||||
"version": "1.0.1",
|
||||
@@ -378,18 +378,18 @@
|
||||
}
|
||||
},
|
||||
"mimic-response": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.0.tgz",
|
||||
"integrity": "sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4="
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
|
||||
"integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
@@ -397,32 +397,32 @@
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
||||
}
|
||||
}
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
|
||||
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA=="
|
||||
"version": "2.11.1",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz",
|
||||
"integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA=="
|
||||
},
|
||||
"node-abi": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.3.0.tgz",
|
||||
"integrity": "sha512-zwm6vU3SsVgw3e9fu48JBaRBCJGIvAgysDsqtf5+vEexFE71bEOtaMWb5zr/zODZNzTPtQlqUUpC79k68Hspow==",
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.4.5.tgz",
|
||||
"integrity": "sha512-aa/UC6Nr3+tqhHGRsAuw/edz7/q9nnetBrKWxj6rpTtm+0X9T1qU7lIEHMS3yN9JwAbRiKUbRRFy1PLz/y3aaA==",
|
||||
"requires": {
|
||||
"semver": "^5.4.1"
|
||||
}
|
||||
},
|
||||
"node-hid": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/node-hid/-/node-hid-0.5.7.tgz",
|
||||
"integrity": "sha512-dwwpOetL2+MGYgivbO22ML+45ieCGbueWv1rYxRgBoEc2QMp6UF6ZucEkYts1IA3YPWJNkmpGh6dqQ85n19szw==",
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/node-hid/-/node-hid-0.7.3.tgz",
|
||||
"integrity": "sha512-LOCqWqcOlng+Kn1Qj/54zrPVfCagg1O7RlSgMmugykBcoYvUud6BswTrJM2aXuBac+bCCm3lA2srRG8YfmyXZQ==",
|
||||
"requires": {
|
||||
"bindings": "^1.3.0",
|
||||
"nan": "^2.6.2",
|
||||
"prebuild-install": "^2.2.2"
|
||||
"nan": "^2.10.0",
|
||||
"prebuild-install": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"noop-logger": {
|
||||
@@ -493,9 +493,9 @@
|
||||
}
|
||||
},
|
||||
"prebuild-install": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.5.1.tgz",
|
||||
"integrity": "sha512-3DX9L6pzwc1m1ksMkW3Ky2WLgPQUBiySOfXVl3WZyAeJSyJb4wtoH9OmeRGcubAWsMlLiL8BTHbwfm/jPQE9Ag==",
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-4.0.0.tgz",
|
||||
"integrity": "sha512-7tayxeYboJX0RbVzdnKyGl2vhQRWr6qfClEXDhOkXjuaOKCw2q8aiuFhONRYVsG/czia7KhpykIlI2S2VaPunA==",
|
||||
"requires": {
|
||||
"detect-libc": "^1.0.3",
|
||||
"expand-template": "^1.0.2",
|
||||
@@ -529,11 +529,11 @@
|
||||
}
|
||||
},
|
||||
"rc": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.6.tgz",
|
||||
"integrity": "sha1-6xiYnG1PTxYsOZ953dKfODVWgJI=",
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
|
||||
"requires": {
|
||||
"deep-extend": "~0.4.0",
|
||||
"deep-extend": "^0.6.0",
|
||||
"ini": "~1.3.0",
|
||||
"minimist": "^1.2.0",
|
||||
"strip-json-comments": "~2.0.1"
|
||||
@@ -567,9 +567,9 @@
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
|
||||
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
|
||||
"version": "5.5.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
|
||||
"integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw=="
|
||||
},
|
||||
"set-blocking": {
|
||||
"version": "2.0.0",
|
||||
@@ -587,9 +587,9 @@
|
||||
"integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
|
||||
},
|
||||
"simple-get": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.7.0.tgz",
|
||||
"integrity": "sha512-RkE9rGPHcxYZ/baYmgJtOSM63vH0Vyq+ma5TijBcLla41SWlh8t6XYIGMR/oeZcmr+/G8k+zrClkkVrtnQ0esg==",
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz",
|
||||
"integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==",
|
||||
"requires": {
|
||||
"decompress-response": "^3.3.0",
|
||||
"once": "^1.3.1",
|
||||
@@ -616,7 +616,7 @@
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
@@ -641,9 +641,9 @@
|
||||
"integrity": "sha1-2kFbc+BeKgf7/lQEAb/8eQxPfdk="
|
||||
},
|
||||
"tar-fs": {
|
||||
"version": "1.16.0",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.0.tgz",
|
||||
"integrity": "sha512-I9rb6v7mjWLtOfCau9eH5L7sLJyU2BnxtEZRQ5Mt+eRKmf1F0ohXmT/Jc3fr52kDvjJ/HV5MH3soQfPL5bQ0Yg==",
|
||||
"version": "1.16.3",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz",
|
||||
"integrity": "sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==",
|
||||
"requires": {
|
||||
"chownr": "^1.0.1",
|
||||
"mkdirp": "^0.5.1",
|
||||
@@ -738,11 +738,11 @@
|
||||
"integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
|
||||
},
|
||||
"wide-align": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
|
||||
"integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
||||
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
|
||||
"requires": {
|
||||
"string-width": "^1.0.2"
|
||||
"string-width": "^1.0.2 || 2"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"command-line-args": "4.0.7",
|
||||
"decompress": "4.2.0",
|
||||
"decompress-bzip2": "4.0.0",
|
||||
"node-hid": "0.5.7",
|
||||
"node-hid": "0.7.3",
|
||||
"sudo-prompt": "7.0.0",
|
||||
"tmp": "0.0.33",
|
||||
"uhk-common": "^1.0.0",
|
||||
@@ -31,10 +31,10 @@
|
||||
"scripts": {
|
||||
"start": "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-blhost",
|
||||
"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",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,11 @@ 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 { setMenu } from './electron-menu';
|
||||
|
||||
const optionDefinitions = [
|
||||
{name: 'addons', type: Boolean},
|
||||
{name: 'spe', type: Boolean}, // simulate privilege escalation error
|
||||
// show 'Lock layer when double tapping this key' checkbox on 'Layer' tab of the config popover
|
||||
{name: 'layer-double-tap', type: Boolean}
|
||||
{name: 'spe', type: Boolean} // simulate privilege escalation error
|
||||
];
|
||||
|
||||
const options: CommandLineArgs = commandLineArgs(optionDefinitions);
|
||||
@@ -101,9 +100,9 @@ function createWindow() {
|
||||
},
|
||||
icon: path.join(__dirname, 'renderer/assets/images/agent-app-icon.png')
|
||||
});
|
||||
win.setMenuBarVisibility(false);
|
||||
setMenu(win);
|
||||
win.maximize();
|
||||
uhkHidDeviceService = new UhkHidDevice(logger, options);
|
||||
uhkHidDeviceService = new UhkHidDevice(logger, options, packagesDir);
|
||||
uhkBlhost = new UhkBlhost(logger, packagesDir);
|
||||
uhkOperations = new UhkOperations(logger, uhkBlhost, uhkHidDeviceService, packagesDir);
|
||||
deviceService = new DeviceService(logger, win, uhkHidDeviceService, uhkOperations, packagesDir);
|
||||
|
||||
37
packages/uhk-agent/src/electron-menu.ts
Normal 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);
|
||||
};
|
||||
@@ -14,6 +14,6 @@
|
||||
"npm": ">=5.1.0 <6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"node-hid": "0.5.7"
|
||||
"node-hid": "0.7.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,15 +23,12 @@ export class AppService extends MainServiceBase {
|
||||
|
||||
private async handleAppStartInfo(event: Electron.Event) {
|
||||
this.logService.info('[AppService] getAppStartInfo');
|
||||
const deviceConnectionState = this.uhkHidDeviceService.getDeviceConnectionState();
|
||||
const deviceConnectionState = await this.uhkHidDeviceService.getDeviceConnectionStateAsync();
|
||||
const response: AppStartInfo = {
|
||||
deviceConnectionState,
|
||||
commandLineArgs: {
|
||||
addons: this.options.addons || false,
|
||||
layerDoubleTap: this.options['layer-double-tap'] || false
|
||||
addons: this.options.addons || false
|
||||
},
|
||||
deviceConnected: deviceConnectionState.connected,
|
||||
hasPermission: deviceConnectionState.hasPermission,
|
||||
bootloaderActive: deviceConnectionState.bootloaderActive,
|
||||
platform: process.platform as string,
|
||||
osVersion: os.release()
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ipcMain } from 'electron';
|
||||
import { isEqual } from 'lodash';
|
||||
import {
|
||||
ConfigurationReply,
|
||||
DeviceConnectionState,
|
||||
@@ -12,15 +13,16 @@ import {
|
||||
SaveUserConfigurationData,
|
||||
UpdateFirmwareData
|
||||
} from 'uhk-common';
|
||||
import { deviceConnectionStateComparer, snooze, UhkHidDevice, UhkOperations } from 'uhk-usb';
|
||||
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/observable/fromPromise';
|
||||
import 'rxjs/add/operator/switchMap';
|
||||
import 'rxjs/add/operator/startWith';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/do';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
|
||||
@@ -253,8 +255,8 @@ export class DeviceService {
|
||||
|
||||
this.pollTimer$ = Observable.interval(1000)
|
||||
.startWith(0)
|
||||
.map(() => this.device.getDeviceConnectionState())
|
||||
.distinctUntilChanged<DeviceConnectionState>(deviceConnectionStateComparer)
|
||||
.switchMap(() => Observable.fromPromise(this.device.getDeviceConnectionStateAsync()))
|
||||
.distinctUntilChanged<DeviceConnectionState>(isEqual)
|
||||
.do((state: DeviceConnectionState) => {
|
||||
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, state);
|
||||
this.logService.info('[DeviceService] Device connection state changed to:', state);
|
||||
|
||||
@@ -454,14 +454,6 @@
|
||||
"scancode": 182
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "132",
|
||||
"text": "Stop/Eject",
|
||||
"additional": {
|
||||
"type": "media",
|
||||
"scancode": 204
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "133",
|
||||
"text": "Play/Pause",
|
||||
@@ -502,14 +494,6 @@
|
||||
"scancode": 184
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "138",
|
||||
"text": "WWW",
|
||||
"additional": {
|
||||
"type": "media",
|
||||
"scancode": 138
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "145",
|
||||
"text": "History Back",
|
||||
@@ -531,14 +515,6 @@
|
||||
{
|
||||
"text": "Launch application",
|
||||
"children": [
|
||||
{
|
||||
"id": "142",
|
||||
"text": "Launch Web Browser",
|
||||
"additional": {
|
||||
"type": "media",
|
||||
"scancode": 406
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "143",
|
||||
"text": "Launch Email Client",
|
||||
@@ -703,5 +679,50 @@
|
||||
"text": "."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "International",
|
||||
"children": [
|
||||
{
|
||||
"id": "235",
|
||||
"text": "International 1",
|
||||
"additional": {
|
||||
"type": "basic",
|
||||
"scancode": 135
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "236",
|
||||
"text": "International 2",
|
||||
"additional": {
|
||||
"type": "basic",
|
||||
"scancode": 136
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "237",
|
||||
"text": "International 3",
|
||||
"additional": {
|
||||
"type": "basic",
|
||||
"scancode": 137
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "244",
|
||||
"text": "Language 1",
|
||||
"additional": {
|
||||
"type": "basic",
|
||||
"scancode": 144
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "245",
|
||||
"text": "Language 2",
|
||||
"additional": {
|
||||
"type": "basic",
|
||||
"scancode": 145
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -4,8 +4,9 @@ import { Keymap } from './keymap';
|
||||
import { Macro } from './macro';
|
||||
import { ModuleConfiguration } from './module-configuration';
|
||||
import { ConfigSerializer } from '../config-serializer';
|
||||
import { KeystrokeAction } from './key-action';
|
||||
import { KeystrokeAction, NoneAction } from './key-action';
|
||||
import { SecondaryRoleAction } from './secondary-role-action';
|
||||
import { isScancodeExists } from './scancode-checker';
|
||||
|
||||
export class UserConfiguration {
|
||||
|
||||
@@ -249,6 +250,10 @@ export class UserConfiguration {
|
||||
keyAction.secondaryRoleAction === SecondaryRoleAction.mouse) {
|
||||
(keyAction as any)._secondaryRoleAction = undefined;
|
||||
}
|
||||
|
||||
if (keyAction.hasScancode() && !isScancodeExists(keyAction.scancode)) {
|
||||
module.keyActions[keyActionId] = new NoneAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { CommandLineArgs } from './command-line-args';
|
||||
import { DeviceConnectionState } from './device-connection-state';
|
||||
|
||||
export interface AppStartInfo {
|
||||
commandLineArgs: CommandLineArgs;
|
||||
deviceConnected: boolean;
|
||||
hasPermission: boolean;
|
||||
bootloaderActive: boolean;
|
||||
deviceConnectionState: DeviceConnectionState;
|
||||
platform: string;
|
||||
osVersion: string;
|
||||
}
|
||||
|
||||
@@ -7,9 +7,4 @@ export interface CommandLineArgs {
|
||||
* simulate privilege escalation error
|
||||
*/
|
||||
spe?: boolean;
|
||||
/**
|
||||
* show 'Lock layer when double tapping this key' checkbox on 'Layer' tab of the config popover
|
||||
* if it false the checkbox invisible and the value of the checkbox = true
|
||||
*/
|
||||
layerDoubleTap?: boolean;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { UdevRulesInfo } from './udev-rules-info';
|
||||
|
||||
export interface DeviceConnectionState {
|
||||
connected: boolean;
|
||||
hasPermission: boolean;
|
||||
bootloaderActive: boolean;
|
||||
zeroInterfaceAvailable: boolean;
|
||||
udevRulesInfo: UdevRulesInfo;
|
||||
}
|
||||
|
||||
@@ -8,4 +8,5 @@ export * from './device-connection-state';
|
||||
export * from './hardware-modules';
|
||||
export * from './hardware-module-info';
|
||||
export * from './save-user-configuration-data';
|
||||
export * from './udev-rules-info';
|
||||
export * from './update-firmware-data';
|
||||
|
||||
16
packages/uhk-common/src/models/udev-rules-info.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* What is the state of the udev rules.
|
||||
* Only on Linux need extra udev rules.
|
||||
*/
|
||||
export enum UdevRulesInfo {
|
||||
Unkonwn,
|
||||
Ok,
|
||||
/**
|
||||
* Udev rules not exists need to setup on Linux
|
||||
*/
|
||||
NeedToSetup,
|
||||
/**
|
||||
* Udev rules exist but different than expected on Linux
|
||||
*/
|
||||
Different
|
||||
}
|
||||
150
packages/uhk-usb/package-lock.json
generated
@@ -21,9 +21,9 @@
|
||||
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
|
||||
"integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
|
||||
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
|
||||
"requires": {
|
||||
"delegates": "^1.0.0",
|
||||
"readable-stream": "^2.0.6"
|
||||
@@ -36,17 +36,36 @@
|
||||
},
|
||||
"bl": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
|
||||
"resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
|
||||
"integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
|
||||
"requires": {
|
||||
"readable-stream": "^2.3.5",
|
||||
"safe-buffer": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"buffer-alloc": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
|
||||
"integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
|
||||
"requires": {
|
||||
"buffer-alloc-unsafe": "^1.1.0",
|
||||
"buffer-fill": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"buffer-alloc-unsafe": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
|
||||
"integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg=="
|
||||
},
|
||||
"buffer-fill": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
|
||||
"integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw="
|
||||
},
|
||||
"chownr": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz",
|
||||
"integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE="
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
|
||||
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g=="
|
||||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
@@ -72,9 +91,9 @@
|
||||
}
|
||||
},
|
||||
"deep-extend": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz",
|
||||
"integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8="
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
|
||||
},
|
||||
"delegates": {
|
||||
"version": "1.0.0",
|
||||
@@ -95,9 +114,14 @@
|
||||
}
|
||||
},
|
||||
"expand-template": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.0.tgz",
|
||||
"integrity": "sha512-kkjwkMqj0h4w/sb32ERCDxCQkREMCAgS39DscDnSwDsbxnwwM1BTZySdC3Bn1lhY7vL08n9GoO/fVTynjDgRyQ=="
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.1.tgz",
|
||||
"integrity": "sha512-cebqLtV8KOZfw0UI8TEFWxtczxxC1jvyUvx6H4fyp1K1FN7A4Q+uggVUlOsI1K8AGU0rwOGqP8nCapdrw8CYQg=="
|
||||
},
|
||||
"fs-constants": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||
},
|
||||
"gauge": {
|
||||
"version": "2.7.4",
|
||||
@@ -148,18 +172,18 @@
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||
},
|
||||
"mimic-response": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.0.tgz",
|
||||
"integrity": "sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4="
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
|
||||
"integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
@@ -167,32 +191,32 @@
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
||||
}
|
||||
}
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
|
||||
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA=="
|
||||
"version": "2.11.1",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz",
|
||||
"integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA=="
|
||||
},
|
||||
"node-abi": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.3.0.tgz",
|
||||
"integrity": "sha512-zwm6vU3SsVgw3e9fu48JBaRBCJGIvAgysDsqtf5+vEexFE71bEOtaMWb5zr/zODZNzTPtQlqUUpC79k68Hspow==",
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.4.5.tgz",
|
||||
"integrity": "sha512-aa/UC6Nr3+tqhHGRsAuw/edz7/q9nnetBrKWxj6rpTtm+0X9T1qU7lIEHMS3yN9JwAbRiKUbRRFy1PLz/y3aaA==",
|
||||
"requires": {
|
||||
"semver": "^5.4.1"
|
||||
}
|
||||
},
|
||||
"node-hid": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/node-hid/-/node-hid-0.5.7.tgz",
|
||||
"integrity": "sha512-dwwpOetL2+MGYgivbO22ML+45ieCGbueWv1rYxRgBoEc2QMp6UF6ZucEkYts1IA3YPWJNkmpGh6dqQ85n19szw==",
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/node-hid/-/node-hid-0.7.3.tgz",
|
||||
"integrity": "sha512-LOCqWqcOlng+Kn1Qj/54zrPVfCagg1O7RlSgMmugykBcoYvUud6BswTrJM2aXuBac+bCCm3lA2srRG8YfmyXZQ==",
|
||||
"requires": {
|
||||
"bindings": "^1.3.0",
|
||||
"nan": "^2.6.2",
|
||||
"prebuild-install": "^2.2.2"
|
||||
"nan": "^2.10.0",
|
||||
"prebuild-install": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"noop-logger": {
|
||||
@@ -235,9 +259,9 @@
|
||||
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
|
||||
},
|
||||
"prebuild-install": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.5.1.tgz",
|
||||
"integrity": "sha512-3DX9L6pzwc1m1ksMkW3Ky2WLgPQUBiySOfXVl3WZyAeJSyJb4wtoH9OmeRGcubAWsMlLiL8BTHbwfm/jPQE9Ag==",
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-4.0.0.tgz",
|
||||
"integrity": "sha512-7tayxeYboJX0RbVzdnKyGl2vhQRWr6qfClEXDhOkXjuaOKCw2q8aiuFhONRYVsG/czia7KhpykIlI2S2VaPunA==",
|
||||
"requires": {
|
||||
"detect-libc": "^1.0.3",
|
||||
"expand-template": "^1.0.2",
|
||||
@@ -271,11 +295,11 @@
|
||||
}
|
||||
},
|
||||
"rc": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.6.tgz",
|
||||
"integrity": "sha1-6xiYnG1PTxYsOZ953dKfODVWgJI=",
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
|
||||
"requires": {
|
||||
"deep-extend": "~0.4.0",
|
||||
"deep-extend": "^0.6.0",
|
||||
"ini": "~1.3.0",
|
||||
"minimist": "^1.2.0",
|
||||
"strip-json-comments": "~2.0.1"
|
||||
@@ -283,7 +307,7 @@
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
@@ -296,14 +320,14 @@
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
|
||||
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
|
||||
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
|
||||
"version": "5.5.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
|
||||
"integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw=="
|
||||
},
|
||||
"set-blocking": {
|
||||
"version": "2.0.0",
|
||||
@@ -321,9 +345,9 @@
|
||||
"integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
|
||||
},
|
||||
"simple-get": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.7.0.tgz",
|
||||
"integrity": "sha512-RkE9rGPHcxYZ/baYmgJtOSM63vH0Vyq+ma5TijBcLla41SWlh8t6XYIGMR/oeZcmr+/G8k+zrClkkVrtnQ0esg==",
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz",
|
||||
"integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==",
|
||||
"requires": {
|
||||
"decompress-response": "^3.3.0",
|
||||
"once": "^1.3.1",
|
||||
@@ -350,7 +374,7 @@
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
@@ -362,9 +386,9 @@
|
||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
|
||||
},
|
||||
"tar-fs": {
|
||||
"version": "1.16.0",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.0.tgz",
|
||||
"integrity": "sha512-I9rb6v7mjWLtOfCau9eH5L7sLJyU2BnxtEZRQ5Mt+eRKmf1F0ohXmT/Jc3fr52kDvjJ/HV5MH3soQfPL5bQ0Yg==",
|
||||
"version": "1.16.3",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz",
|
||||
"integrity": "sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==",
|
||||
"requires": {
|
||||
"chownr": "^1.0.1",
|
||||
"mkdirp": "^0.5.1",
|
||||
@@ -384,16 +408,24 @@
|
||||
}
|
||||
},
|
||||
"tar-stream": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.5.tgz",
|
||||
"integrity": "sha512-mQdgLPc/Vjfr3VWqWbfxW8yQNiJCbAZ+Gf6GDu1Cy0bdb33ofyiNGBtAY96jHFhDuivCwgW1H9DgTON+INiXgg==",
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz",
|
||||
"integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==",
|
||||
"requires": {
|
||||
"bl": "^1.0.0",
|
||||
"buffer-alloc": "^1.2.0",
|
||||
"end-of-stream": "^1.0.0",
|
||||
"readable-stream": "^2.0.0",
|
||||
"fs-constants": "^1.0.0",
|
||||
"readable-stream": "^2.3.0",
|
||||
"to-buffer": "^1.1.1",
|
||||
"xtend": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"to-buffer": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz",
|
||||
"integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg=="
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
@@ -413,11 +445,11 @@
|
||||
"integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
|
||||
},
|
||||
"wide-align": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
|
||||
"integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
||||
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
|
||||
"requires": {
|
||||
"string-width": "^1.0.2"
|
||||
"string-width": "^1.0.2 || 2"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"@types/node": "8.0.28"
|
||||
},
|
||||
"dependencies": {
|
||||
"node-hid": "0.5.7",
|
||||
"node-hid": "0.7.3",
|
||||
"uhk-common": "1.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { Device, devices, HID } from 'node-hid';
|
||||
import { CommandLineArgs, DeviceConnectionState, isEqualArray, LogService } from 'uhk-common';
|
||||
import { pathExists } from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import { platform } from 'os';
|
||||
import { CommandLineArgs, DeviceConnectionState, isEqualArray, LogService, UdevRulesInfo } from 'uhk-common';
|
||||
|
||||
import {
|
||||
ConfigBufferId,
|
||||
@@ -13,7 +16,7 @@ import {
|
||||
UsbCommand,
|
||||
UsbVariables
|
||||
} from './constants';
|
||||
import { bufferToString, getTransferData, isUhkDevice, retry, snooze } from './util';
|
||||
import { bufferToString, getFileContentAsync, getTransferData, isUhkDevice, isUhkZeroInterface, retry, snooze } from './util';
|
||||
|
||||
export const BOOTLOADER_TIMEOUT_MS = 5000;
|
||||
|
||||
@@ -28,9 +31,11 @@ export class UhkHidDevice {
|
||||
private _prevDevices = [];
|
||||
private _device: HID;
|
||||
private _hasPermission = false;
|
||||
private _udevRulesInfo = UdevRulesInfo.Unkonwn;
|
||||
|
||||
constructor(private logService: LogService,
|
||||
private options: CommandLineArgs) {
|
||||
private options: CommandLineArgs,
|
||||
private rootDir: string) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,7 +55,11 @@ export class UhkHidDevice {
|
||||
return true;
|
||||
}
|
||||
|
||||
const dev = devices().find((x: Device) => isUhkDevice(x) || x.productId === Constants.BOOTLOADER_ID);
|
||||
this.logService.debug('[UhkHidDevice] Devices before check permission:');
|
||||
const devs = devices();
|
||||
this.logDevices(devs);
|
||||
|
||||
const dev = devs.find((x: Device) => isUhkZeroInterface(x) || x.productId === Constants.BOOTLOADER_ID);
|
||||
|
||||
if (!dev) {
|
||||
return true;
|
||||
@@ -70,20 +79,26 @@ export class UhkHidDevice {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return with true is an UHK Device is connected to the computer.
|
||||
* @returns {boolean}
|
||||
* Return with the USB device communication sate.
|
||||
* @returns {DeviceConnectionState}
|
||||
*/
|
||||
public getDeviceConnectionState(): DeviceConnectionState {
|
||||
public async getDeviceConnectionStateAsync(): Promise<DeviceConnectionState> {
|
||||
const devs = devices();
|
||||
const result: DeviceConnectionState = {
|
||||
bootloaderActive: false,
|
||||
connected: false,
|
||||
hasPermission: this.hasPermission()
|
||||
zeroInterfaceAvailable: false,
|
||||
hasPermission: this.hasPermission(),
|
||||
udevRulesInfo: await this.getUdevInfoAsync()
|
||||
};
|
||||
|
||||
for (const dev of devs) {
|
||||
if (isUhkDevice(dev)) {
|
||||
result.connected = true;
|
||||
}
|
||||
|
||||
if (isUhkZeroInterface(dev)) {
|
||||
result.zeroInterfaceAvailable = true;
|
||||
} else if (dev.vendorId === Constants.VENDOR_ID &&
|
||||
dev.productId === Constants.BOOTLOADER_ID) {
|
||||
result.bootloaderActive = true;
|
||||
@@ -258,17 +273,25 @@ export class UhkHidDevice {
|
||||
private connectToDevice(): HID {
|
||||
try {
|
||||
const devs = devices();
|
||||
if (!isEqualArray(this._prevDevices, devs)) {
|
||||
this.logService.debug('[UhkHidDevice] Available devices:');
|
||||
for (const logDevice of devs) {
|
||||
this.logService.debug(JSON.stringify(logDevice));
|
||||
let compareDevices = devs as any;
|
||||
|
||||
if (platform() === 'linux') {
|
||||
compareDevices = devs.map(x => ({
|
||||
productId: x.productId,
|
||||
vendorId: x.vendorId,
|
||||
interface: x.interface
|
||||
}));
|
||||
}
|
||||
this._prevDevices = devs;
|
||||
|
||||
if (!isEqualArray(this._prevDevices, compareDevices)) {
|
||||
this.logService.debug('[UhkHidDevice] Available devices:');
|
||||
this.logDevices(devs);
|
||||
this._prevDevices = compareDevices;
|
||||
} else {
|
||||
this.logService.debug('[UhkHidDevice] Available devices unchanged');
|
||||
}
|
||||
|
||||
const dev = devs.find(isUhkDevice);
|
||||
const dev = devs.find(isUhkZeroInterface);
|
||||
|
||||
if (!dev) {
|
||||
this.logService.debug('[UhkHidDevice] UHK Device not found:');
|
||||
@@ -277,13 +300,43 @@ export class UhkHidDevice {
|
||||
const device = new HID(dev.path);
|
||||
this.logService.debug('[UhkHidDevice] Used device:', JSON.stringify(dev));
|
||||
return device;
|
||||
}
|
||||
catch (err) {
|
||||
} catch (err) {
|
||||
this.logService.error('[UhkHidDevice] Can not create device:', err);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private logDevices(devs: Array<Device>): void {
|
||||
for (const logDevice of devs) {
|
||||
this.logService.debug(JSON.stringify(logDevice));
|
||||
}
|
||||
}
|
||||
|
||||
private async getUdevInfoAsync(): Promise<UdevRulesInfo> {
|
||||
if (this._udevRulesInfo === UdevRulesInfo.Ok) {
|
||||
return UdevRulesInfo.Ok;
|
||||
}
|
||||
|
||||
if (process.platform === 'win32' || process.platform === 'darwin') {
|
||||
this._udevRulesInfo = UdevRulesInfo.Ok;
|
||||
return UdevRulesInfo.Ok;
|
||||
}
|
||||
|
||||
if (!(await pathExists('/etc/udev/rules.d/50-uhk60.rules'))) {
|
||||
return UdevRulesInfo.NeedToSetup;
|
||||
}
|
||||
|
||||
const expectedUdevSettings = await getFileContentAsync(path.join(this.rootDir, 'rules/50-uhk60.rules'));
|
||||
const currentUdevSettings = await getFileContentAsync('/etc/udev/rules.d/50-uhk60.rules');
|
||||
|
||||
if (isEqualArray(expectedUdevSettings, currentUdevSettings)) {
|
||||
this._udevRulesInfo = UdevRulesInfo.Ok;
|
||||
return UdevRulesInfo.Ok;
|
||||
}
|
||||
|
||||
return UdevRulesInfo.Different;
|
||||
}
|
||||
}
|
||||
|
||||
function kbootCommandName(module: ModuleSlotToI2cAddress): string {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Device } from 'node-hid';
|
||||
import { DeviceConnectionState, LogService } from 'uhk-common';
|
||||
import { readFile } from 'fs-extra';
|
||||
import { EOL } from 'os';
|
||||
import { LogService } from 'uhk-common';
|
||||
|
||||
import { Constants, UsbCommand } from './constants';
|
||||
|
||||
@@ -98,13 +100,7 @@ export async function retry(command: Function, maxTry = 3, logService?: LogServi
|
||||
}
|
||||
}
|
||||
|
||||
export const deviceConnectionStateComparer = (a: DeviceConnectionState, b: DeviceConnectionState): boolean => {
|
||||
return a.hasPermission === b.hasPermission
|
||||
&& a.connected === b.connected
|
||||
&& a.bootloaderActive === b.bootloaderActive;
|
||||
};
|
||||
|
||||
export const isUhkDevice = (dev: Device): boolean => {
|
||||
export const isUhkZeroInterface = (dev: Device): boolean => {
|
||||
return dev.vendorId === Constants.VENDOR_ID &&
|
||||
dev.productId === Constants.PRODUCT_ID &&
|
||||
// hidapi can not read the interface number on Mac, so check the usage page and usage
|
||||
@@ -112,3 +108,17 @@ export const isUhkDevice = (dev: Device): boolean => {
|
||||
(dev.usagePage === (0xFF00 | 0x00) && dev.usage === 0x01) || // New firmware
|
||||
dev.interface === 0);
|
||||
};
|
||||
|
||||
export const isUhkDevice = (dev: Device): boolean => {
|
||||
return dev.vendorId === Constants.VENDOR_ID &&
|
||||
(dev.productId === Constants.PRODUCT_ID || dev.productId === Constants.BOOTLOADER_ID);
|
||||
};
|
||||
|
||||
export const getFileContentAsync = async (filePath: string): Promise<Array<string>> => {
|
||||
const fileContent = await readFile(filePath, {encoding: 'utf-8'});
|
||||
|
||||
return fileContent
|
||||
.split(EOL)
|
||||
.map(x => x.trim())
|
||||
.filter(x => !x.startsWith('#') && x.length > 0);
|
||||
};
|
||||
|
||||
6
packages/uhk-web/package-lock.json
generated
@@ -9540,9 +9540,9 @@
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
|
||||
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
|
||||
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
|
||||
"dev": true
|
||||
},
|
||||
"semver-dsl": {
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
"reselect": "3.0.1",
|
||||
"rxjs": "5.5.8",
|
||||
"typescript": "2.6.2",
|
||||
"semver": "5.6.0",
|
||||
"uhk-common": "1.0.0",
|
||||
"xml-loader": "1.2.1",
|
||||
"zone.js": "0.8.26",
|
||||
|
||||
@@ -13,7 +13,8 @@ import {
|
||||
getShowAppUpdateAvailable,
|
||||
deviceConfigurationLoaded,
|
||||
runningInElectron,
|
||||
saveToKeyboardState
|
||||
saveToKeyboardState,
|
||||
keypressCapturing
|
||||
} from './store';
|
||||
import { ProgressButtonState } from './store/reducers/progress-button-state';
|
||||
|
||||
@@ -42,7 +43,9 @@ export class MainAppComponent implements OnDestroy {
|
||||
runningInElectron$: Observable<boolean>;
|
||||
saveToKeyboardState: ProgressButtonState;
|
||||
|
||||
private keypressCapturing: boolean;
|
||||
private saveToKeyboardStateSubscription: Subscription;
|
||||
private keypressCapturingSubscription: Subscription;
|
||||
|
||||
constructor(private store: Store<AppState>) {
|
||||
this.showUpdateAvailable$ = store.select(getShowAppUpdateAvailable);
|
||||
@@ -50,10 +53,13 @@ export class MainAppComponent implements OnDestroy {
|
||||
this.runningInElectron$ = store.select(runningInElectron);
|
||||
this.saveToKeyboardStateSubscription = store.select(saveToKeyboardState)
|
||||
.subscribe(data => this.saveToKeyboardState = data);
|
||||
this.keypressCapturingSubscription = store.select(keypressCapturing)
|
||||
.subscribe(data => this.keypressCapturing = data);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.saveToKeyboardStateSubscription.unsubscribe();
|
||||
this.keypressCapturingSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
@HostListener('document:keydown', ['$event'])
|
||||
@@ -61,7 +67,8 @@ export class MainAppComponent implements OnDestroy {
|
||||
if (this.saveToKeyboardState.showButton &&
|
||||
event.ctrlKey &&
|
||||
event.key === 's' &&
|
||||
!event.defaultPrevented) {
|
||||
!event.defaultPrevented &&
|
||||
!this.keypressCapturing) {
|
||||
this.clickedOnProgressButton(this.saveToKeyboardState.action);
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
@@ -8,4 +8,3 @@
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import 'rxjs/add/operator/combineLatest';
|
||||
|
||||
import { saveAs } from 'file-saver';
|
||||
|
||||
import { allowLayerDoubleTap, AppState, getKeyboardLayout } from '../../../store';
|
||||
import { layerDoubleTapSupported, AppState, getKeyboardLayout } from '../../../store';
|
||||
import { getKeymap, getKeymaps, getUserConfiguration } from '../../../store/reducers/user-configuration';
|
||||
import { KeyboardLayout } from '../../../keyboard/keyboard-layout.enum';
|
||||
import { KeymapActions } from '../../../store/actions';
|
||||
@@ -51,7 +51,7 @@ export class KeymapEditComponent {
|
||||
.map((keymaps: Keymap[]) => keymaps.length > 1);
|
||||
|
||||
this.keyboardLayout$ = store.select(getKeyboardLayout);
|
||||
this.allowLayerDoubleTap$ = store.select(allowLayerDoubleTap);
|
||||
this.allowLayerDoubleTap$ = store.select(layerDoubleTapSupported);
|
||||
}
|
||||
|
||||
downloadKeymap() {
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
Component,
|
||||
ElementRef,
|
||||
Input,
|
||||
Renderer,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { TextMacroAction } from 'uhk-common';
|
||||
@@ -23,14 +22,14 @@ export class MacroTextTabComponent extends MacroBaseComponent implements OnInit,
|
||||
@Input() macroAction: TextMacroAction;
|
||||
@ViewChild('macroTextInput') input: ElementRef;
|
||||
|
||||
constructor(private renderer: Renderer) { super(); }
|
||||
constructor() { super(); }
|
||||
|
||||
ngOnInit() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.renderer.invokeElementMethod(this.input.nativeElement, 'focus');
|
||||
this.input.nativeElement.focus();
|
||||
}
|
||||
|
||||
onTextChange() {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
></macro-header>
|
||||
<macro-list
|
||||
[macro]="macro"
|
||||
[macroPlaybackSupported]="macroPlaybackSupported$ | async"
|
||||
(add)="addAction($event.macroId, $event.action)"
|
||||
(edit)="editAction($event.macroId, $event.index, $event.action)"
|
||||
(delete)="deleteAction($event.macroId, $event.index, $event.action)"
|
||||
|
||||
@@ -3,11 +3,12 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Macro, MacroAction } from 'uhk-common';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import 'rxjs/add/operator/pluck';
|
||||
|
||||
import { MacroActions } from '../../../store/actions';
|
||||
import { AppState } from '../../../store';
|
||||
import { AppState, macroPlaybackSupported } from '../../../store';
|
||||
import { getMacro } from '../../../store/reducers/user-configuration';
|
||||
|
||||
@Component({
|
||||
@@ -22,6 +23,7 @@ export class MacroEditComponent implements OnDestroy {
|
||||
macro: Macro;
|
||||
isNew: boolean;
|
||||
macroId: number;
|
||||
macroPlaybackSupported$: Observable<boolean>;
|
||||
|
||||
private subscription: Subscription;
|
||||
constructor(private store: Store<AppState>, public route: ActivatedRoute) {
|
||||
@@ -37,6 +39,7 @@ export class MacroEditComponent implements OnDestroy {
|
||||
});
|
||||
|
||||
this.isNew = this.route.snapshot.params['empty'] === 'new';
|
||||
this.macroPlaybackSupported$ = this.store.select(macroPlaybackSupported);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
|
||||
@@ -155,7 +155,7 @@ export class MacroItemComponent implements OnInit, OnChanges {
|
||||
// Tap/press/release modifiers
|
||||
for (let i = KeyModifiers.leftCtrl; i <= KeyModifiers.rightGui; i <<= 1) {
|
||||
if (action.isModifierActive(i)) {
|
||||
this.title += ' ' + KeyModifiers[i];
|
||||
this.title += ' ' + this.mapper.getOsSpecificModifierTextByValue(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="row list-container">
|
||||
<div class="col-xs-10 col-xs-offset-1 list-group">
|
||||
<p><i>Please note that macro playback is not implemented yet. You can create macros, but they won't have any effect until firmware support is implemented. We're working on this.</i></p>
|
||||
<p *ngIf="!macroPlaybackSupported"><i>Please note that macro playback is not implemented yet. You can create macros, but they won't have any effect until firmware support is implemented. We're working on this.</i></p>
|
||||
<div class="macro-actions-container" [dragula]="'macroActions'" [dragulaModel]="macro.macroActions">
|
||||
<macro-item *ngFor="let macroAction of macro.macroActions; let macroActionIndex = index"
|
||||
[macroAction]="macroAction"
|
||||
|
||||
@@ -35,6 +35,7 @@ import { MacroItemComponent } from '../item';
|
||||
})
|
||||
export class MacroListComponent {
|
||||
@Input() macro: Macro;
|
||||
@Input() macroPlaybackSupported: boolean;
|
||||
@ViewChildren(forwardRef(() => MacroItemComponent)) macroItems: QueryList<MacroItemComponent>;
|
||||
|
||||
@Output() add = new EventEmitter();
|
||||
|
||||
@@ -1 +1 @@
|
||||
<uhk-message header="Cannot find your UHK" subtitle="Please plug it in!"></uhk-message>
|
||||
<uhk-message [header]="state.header" [subtitle]="state.subtitle"></uhk-message>
|
||||
|
||||
@@ -1,14 +1,27 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
import 'rxjs/add/operator/ignoreElements';
|
||||
import 'rxjs/add/operator/takeWhile';
|
||||
import { AppState, getMissingDeviceState } from '../../store';
|
||||
import { MissingDeviceState } from '../../models/missing-device-state';
|
||||
|
||||
@Component({
|
||||
selector: 'missing-device',
|
||||
templateUrl: './missing-device.component.html'
|
||||
})
|
||||
export class MissingDeviceComponent {
|
||||
export class MissingDeviceComponent implements OnDestroy {
|
||||
|
||||
constructor() {}
|
||||
state: MissingDeviceState;
|
||||
|
||||
private stateSubscription: Subscription;
|
||||
|
||||
constructor(private store: Store<AppState>) {
|
||||
this.stateSubscription = this.store
|
||||
.select(getMissingDeviceState)
|
||||
.subscribe(state => this.state = state);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.stateSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,40 +8,14 @@
|
||||
<div class="arrowCustom"></div>
|
||||
<div class="popover-title menu-tabs">
|
||||
<ul class="nav nav-tabs popover-menu">
|
||||
<li #keypress [class.active]="activeTab === tabName.Keypress" (click)="selectTab(tabName.Keypress)">
|
||||
<li *ngFor="let tab of tabHeaders; trackBy:trackTabHeader"
|
||||
[class.active]="activeTab === tab.tabName"
|
||||
[class.disabled]="tab.disabled"
|
||||
(click)="selectTab(tab)">
|
||||
<a class="menu-tabs--item">
|
||||
<i class="fa fa-keyboard-o"></i>
|
||||
<span>Keypress</span>
|
||||
</a>
|
||||
</li>
|
||||
<li #layer [class.active]="activeTab === tabName.Layer" (click)="selectTab(tabName.Layer)">
|
||||
<a class="menu-tabs--item">
|
||||
<i class="fa fa-clone"></i>
|
||||
<span>Layer</span>
|
||||
</a>
|
||||
</li>
|
||||
<li #mouse [class.active]="activeTab === tabName.Mouse" (click)="selectTab(tabName.Mouse)">
|
||||
<a class="menu-tabs--item">
|
||||
<i class="fa fa-mouse-pointer"></i>
|
||||
<span>Mouse</span>
|
||||
</a>
|
||||
</li>
|
||||
<li #macro [class.active]="activeTab === tabName.Macro" (click)="selectTab(tabName.Macro)">
|
||||
<a class="menu-tabs--item">
|
||||
<i class="fa fa-play"></i>
|
||||
<span>Macro</span>
|
||||
</a>
|
||||
</li>
|
||||
<li #keymap [class.active]="activeTab === tabName.Keymap" (click)="selectTab(tabName.Keymap)">
|
||||
<a class="menu-tabs--item">
|
||||
<i class="fa fa-keyboard-o"></i>
|
||||
<span>Keymap</span>
|
||||
</a>
|
||||
</li>
|
||||
<li #none [class.active]="activeTab === tabName.None" (click)="selectTab(tabName.None)">
|
||||
<a class="menu-tabs--item">
|
||||
<i class="fa fa-ban"></i>
|
||||
<span>None</span>
|
||||
<i class="fa"
|
||||
[ngClass]="tab.icon"></i>
|
||||
<span>{{ tab.text }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -53,30 +27,31 @@
|
||||
[allowRemapOnAllKeymapWarning]="true"
|
||||
[remapInfo]="remapInfo"
|
||||
[showLayerSwitcherInSecondaryRoles]="currentLayer === 0"
|
||||
(validAction)="keyActionValid=$event"
|
||||
(validAction)="setKeyActionValidState($event)"
|
||||
(keyActionChange)="keystrokeActionChange($event)"
|
||||
></keypress-tab>
|
||||
<layer-tab #tab *ngSwitchCase="tabName.Layer" class="popover-content"
|
||||
[defaultKeyAction]="defaultKeyAction"
|
||||
[currentLayer]="currentLayer"
|
||||
[allowLayerDoubleTap]="allowLayerDoubleTap"
|
||||
(validAction)="keyActionValid=$event"
|
||||
(validAction)="setKeyActionValidState($event)"
|
||||
></layer-tab>
|
||||
<mouse-tab #tab *ngSwitchCase="tabName.Mouse" class="popover-content"
|
||||
[defaultKeyAction]="defaultKeyAction"
|
||||
(validAction)="keyActionValid=$event"
|
||||
(validAction)="setKeyActionValidState($event)"
|
||||
></mouse-tab>
|
||||
<macro-tab #tab *ngSwitchCase="tabName.Macro" class="popover-content"
|
||||
[defaultKeyAction]="defaultKeyAction"
|
||||
(validAction)="keyActionValid=$event"
|
||||
[macroPlaybackSupported]="macroPlaybackSupported$ | async"
|
||||
(validAction)="setKeyActionValidState($event)"
|
||||
></macro-tab>
|
||||
<keymap-tab #tab *ngSwitchCase="tabName.Keymap" class="popover-content"
|
||||
[defaultKeyAction]="defaultKeyAction"
|
||||
[keymaps]="keymaps$ | async"
|
||||
(validAction)="keyActionValid=$event"
|
||||
(validAction)="setKeyActionValidState($event)"
|
||||
></keymap-tab>
|
||||
<none-tab #tab *ngSwitchCase="tabName.None" class="popover-content"
|
||||
(validAction)="keyActionValid=$event"
|
||||
(validAction)="setKeyActionValidState($event)"
|
||||
></none-tab>
|
||||
</div>
|
||||
<div class="popover-action">
|
||||
|
||||
@@ -28,6 +28,11 @@
|
||||
|
||||
.nav-tabs > li {
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.arrowCustom {
|
||||
@@ -85,7 +90,6 @@
|
||||
.menu-tabs--item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
i {
|
||||
margin-right: 0.25em;
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
|
||||
import { Tab } from './tab';
|
||||
|
||||
import { AppState } from '../../store';
|
||||
import { AppState, macroPlaybackSupported } from '../../store';
|
||||
import { getKeymaps } from '../../store/reducers/user-configuration';
|
||||
import { KeyActionRemap } from '../../models/key-action-remap';
|
||||
import { RemapInfo } from '../../models/remap-info';
|
||||
@@ -46,6 +46,13 @@ enum TabName {
|
||||
None
|
||||
}
|
||||
|
||||
export interface TabHeader {
|
||||
text: string;
|
||||
icon: string;
|
||||
tabName: TabName;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'popover',
|
||||
templateUrl: './popover.component.html',
|
||||
@@ -108,6 +115,39 @@ export class PopoverComponent implements OnChanges {
|
||||
animationState: string;
|
||||
shadowKeyAction: KeyAction;
|
||||
disableRemapOnAllLayer = false;
|
||||
tabHeaders: TabHeader[] = [
|
||||
{
|
||||
tabName: TabName.Keypress,
|
||||
icon: 'fa-keyboard-o',
|
||||
text: 'Keypress'
|
||||
},
|
||||
{
|
||||
tabName: TabName.Layer,
|
||||
icon: 'fa-clone',
|
||||
text: 'Layer'
|
||||
},
|
||||
{
|
||||
tabName: TabName.Mouse,
|
||||
icon: 'fa-mouse-pointer',
|
||||
text: 'Mouse'
|
||||
},
|
||||
{
|
||||
tabName: TabName.Macro,
|
||||
icon: 'fa-play',
|
||||
text: 'Macro'
|
||||
},
|
||||
{
|
||||
tabName: TabName.Keymap,
|
||||
icon: 'fa-keyboard-o',
|
||||
text: 'Keymap'
|
||||
},
|
||||
{
|
||||
tabName: TabName.None,
|
||||
icon: 'fa-ban',
|
||||
text: 'None'
|
||||
}
|
||||
];
|
||||
macroPlaybackSupported$: Observable<boolean>;
|
||||
|
||||
private readonly currentKeymap$ = new BehaviorSubject<Keymap>(undefined);
|
||||
|
||||
@@ -119,6 +159,7 @@ export class PopoverComponent implements OnChanges {
|
||||
.map(([keymaps, currentKeymap]: [Keymap[], Keymap]) =>
|
||||
keymaps.filter((keymap: Keymap) => currentKeymap.abbreviation !== keymap.abbreviation)
|
||||
);
|
||||
this.macroPlaybackSupported$ = store.select(macroPlaybackSupported);
|
||||
}
|
||||
|
||||
ngOnChanges(change: SimpleChanges) {
|
||||
@@ -127,24 +168,30 @@ export class PopoverComponent implements OnChanges {
|
||||
}
|
||||
|
||||
if (change['defaultKeyAction']) {
|
||||
let tab: TabName;
|
||||
let tab: TabHeader;
|
||||
this.disableRemapOnAllLayer = false;
|
||||
|
||||
if (this.defaultKeyAction instanceof KeystrokeAction) {
|
||||
this.keystrokeActionChange(this.defaultKeyAction);
|
||||
tab = TabName.Keypress;
|
||||
tab = this.tabHeaders[0];
|
||||
} else if (this.defaultKeyAction instanceof SwitchLayerAction) {
|
||||
tab = TabName.Layer;
|
||||
tab = this.tabHeaders[1];
|
||||
} else if (this.defaultKeyAction instanceof MouseAction) {
|
||||
tab = TabName.Mouse;
|
||||
tab = this.tabHeaders[2];
|
||||
} else if (this.defaultKeyAction instanceof PlayMacroAction) {
|
||||
tab = TabName.Macro;
|
||||
tab = this.tabHeaders[3];
|
||||
} else if (this.defaultKeyAction instanceof SwitchKeymapAction) {
|
||||
tab = TabName.Keymap;
|
||||
tab = this.tabHeaders[4];
|
||||
} else {
|
||||
tab = TabName.None;
|
||||
tab = this.tabHeaders[5];
|
||||
}
|
||||
|
||||
for (const tabHeader of this.tabHeaders) {
|
||||
const allowOnlyLayerTab = tab.tabName === TabName.Layer && this.currentLayer !== 0;
|
||||
|
||||
tabHeader.disabled = allowOnlyLayerTab && tabHeader.tabName !== TabName.Layer;
|
||||
console.log(tabHeader);
|
||||
}
|
||||
this.selectTab(tab);
|
||||
}
|
||||
|
||||
@@ -193,9 +240,13 @@ export class PopoverComponent implements OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
selectTab(tab: TabName): void {
|
||||
this.activeTab = tab;
|
||||
if (tab === TabName.Keypress) {
|
||||
selectTab(tab: TabHeader): void {
|
||||
if (tab.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeTab = tab.tabName;
|
||||
if (tab.tabName === TabName.Keypress) {
|
||||
this.keystrokeActionChange(this.defaultKeyAction as KeystrokeAction);
|
||||
}
|
||||
}
|
||||
@@ -228,6 +279,15 @@ export class PopoverComponent implements OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
setKeyActionValidState($event: boolean): void {
|
||||
this.keyActionValid = $event;
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
trackTabHeader(index: number, tabItem: TabHeader): string {
|
||||
return tabItem.tabName.toString();
|
||||
}
|
||||
|
||||
private calculatePosition() {
|
||||
const offsetLeft: number = this.wrapPosition.left + 265; // 265 is a width of the side menu with a margin
|
||||
const popover: HTMLElement = this.popoverHost.nativeElement;
|
||||
|
||||
@@ -63,7 +63,7 @@ export class LayerTabComponent extends Tab implements OnChanges {
|
||||
this.isNotBase = this.currentLayer > 0;
|
||||
}
|
||||
|
||||
this.validAction.emit(true);
|
||||
this.validAction.emit(!this.isNotBase);
|
||||
}
|
||||
|
||||
keyActionValid(): boolean {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<span> No macros are available to choose from. Create a macro first! </span>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="macroOptions.length > 0">
|
||||
<p><i>Please note that macro playback is not implemented yet. You can bind macros, but they won't have any effect until firmware support is implemented. We're working on this.</i></p>
|
||||
<p *ngIf="!macroPlaybackSupported"><i>Please note that macro playback is not implemented yet. You can bind macros, but they won't have any effect until firmware support is implemented. We're working on this.</i></p>
|
||||
<div class="macro-selector">
|
||||
<b> Play macro: </b>
|
||||
<ngx-select [items]="macroOptions"
|
||||
|
||||
@@ -17,6 +17,7 @@ import { SelectOptionData } from '../../../../models/select-option-data';
|
||||
})
|
||||
export class MacroTabComponent extends Tab implements OnInit, OnChanges, OnDestroy {
|
||||
@Input() defaultKeyAction: KeyAction;
|
||||
@Input() macroPlaybackSupported: boolean;
|
||||
|
||||
macros: Macro[];
|
||||
macroOptions: Array<SelectOptionData>;
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, HostListener, Input, Output } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { CaptureService } from '../../../../services/capture.service';
|
||||
import { KeyModifierModel } from '../../../../models/key-modifier-model';
|
||||
import { AppState } from '../../../../store';
|
||||
import { StartKeypressCapturingAction, StopKeypressCapturingAction } from '../../../../store/actions/app';
|
||||
|
||||
@Component({
|
||||
selector: 'capture-keystroke-button',
|
||||
@@ -17,7 +21,8 @@ export class CaptureKeystrokeButtonComponent {
|
||||
private first: boolean; // enable usage of Enter to start capturing
|
||||
private scanCodePressed: boolean;
|
||||
|
||||
constructor(private captureService: CaptureService) {
|
||||
constructor(private captureService: CaptureService,
|
||||
private store: Store<AppState>) {
|
||||
this.record = false;
|
||||
this.captureService.initModifiers();
|
||||
this.captureService.populateMapping();
|
||||
@@ -28,9 +33,11 @@ export class CaptureKeystrokeButtonComponent {
|
||||
onKeyUp(e: KeyboardEvent) {
|
||||
if (this.scanCodePressed) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.scanCodePressed = false;
|
||||
} else if (this.record && !this.first) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.saveScanCode();
|
||||
}
|
||||
}
|
||||
@@ -54,6 +61,7 @@ export class CaptureKeystrokeButtonComponent {
|
||||
} else if (code === enter) {
|
||||
this.record = true;
|
||||
this.first = true;
|
||||
this.store.dispatch(new StartKeypressCapturingAction());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +73,7 @@ export class CaptureKeystrokeButtonComponent {
|
||||
|
||||
start(): void {
|
||||
this.record = true;
|
||||
this.store.dispatch(new StartKeypressCapturingAction());
|
||||
}
|
||||
|
||||
private saveScanCode(code?: number) {
|
||||
@@ -84,5 +93,6 @@ export class CaptureKeystrokeButtonComponent {
|
||||
private reset() {
|
||||
this.first = false;
|
||||
this.captureService.initModifiers();
|
||||
this.store.dispatch(new StopKeypressCapturingAction());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,14 @@
|
||||
<uhk-message header="Cannot talk to your UHK"
|
||||
subtitle="Your UHK has been detected, but its permissions are not set up yet, so Agent can't talk to it."></uhk-message>
|
||||
|
||||
<button class="btn btn-default btn-lg btn-primary"
|
||||
(click)="setUpPermissions()"> Set up permissions
|
||||
<div *ngIf="state.updateUdevRules">
|
||||
You seem to have an old udev rule file installed. New Agent versions require and updated udev rule file to find your UHK.
|
||||
</div>
|
||||
|
||||
<button class="btn btn-default btn-lg btn-primary mt-10"
|
||||
(click)="setUpPermissions()">
|
||||
<span *ngIf="!state.updateUdevRules">Set up permissions</span>
|
||||
<span *ngIf="state.updateUdevRules">Update udev rule file</span>
|
||||
</button>
|
||||
|
||||
<div class="mt-10">
|
||||
@@ -22,21 +28,7 @@
|
||||
|
||||
<div *ngIf="state.showWhatWillThisDoContent">
|
||||
If you want to set up permissions manually:
|
||||
<ol>
|
||||
<li>Open a terminal.</li>
|
||||
<li>Run <code>su</code> to become root.</li>
|
||||
<li>Paste the following script, and <a class="link-inline" (click)="retry()">retry</a>.</li>
|
||||
</ol>
|
||||
|
||||
<div class="copy-container">
|
||||
<span class="fa fa-2x fa-copy"
|
||||
ngxClipboard
|
||||
[cbContent]="command"
|
||||
title="Copy to clipboard"
|
||||
data-toggle="tooltip"
|
||||
data-placement="top"></span>
|
||||
<pre><code>{{ command }}</code></pre>
|
||||
</div>
|
||||
<udev-rules></udev-rules>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -18,15 +18,6 @@ export class PrivilegeCheckerComponent implements OnInit, OnDestroy {
|
||||
|
||||
state: PrivilagePageSate;
|
||||
|
||||
command = `cat <<EOF >/etc/udev/rules.d/50-uhk60.rules
|
||||
# Ultimate Hacking Keyboard rules
|
||||
# These are the udev rules for accessing the USB interfaces of the UHK as non-root users.
|
||||
# Copy this file to /etc/udev/rules.d and physically reconnect the UHK afterwards.
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="612[0-7]", MODE:="0666"
|
||||
EOF
|
||||
udevadm trigger
|
||||
udevadm settle`;
|
||||
|
||||
private stateSubscription: Subscription;
|
||||
|
||||
constructor(private store: Store<AppState>,
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
[attr.x]="0"
|
||||
[attr.y]="textY"
|
||||
[attr.text-anchor]="'middle'"
|
||||
[attr.font-size]="11">
|
||||
<tspan [attr.x]="spanX">{{ text }}</tspan>
|
||||
[attr.font-size]="fontSize">
|
||||
<tspan [attr.x]="spanX" [attr.dy]="text1Y">{{ text1 }}</tspan>
|
||||
<tspan [attr.x]="spanX" [attr.dy]="text2Y">{{ text2 }}</tspan>
|
||||
</svg:text>
|
||||
<svg:g svg-secondary-role
|
||||
*ngIf="secondaryText"
|
||||
|
||||
|
Before Width: | Height: | Size: 499 B After Width: | Height: | Size: 596 B |
@@ -2,6 +2,7 @@ import { Component, Input, ChangeDetectionStrategy, OnChanges, SimpleChanges } f
|
||||
|
||||
import { isRectangleAsSecondaryRoleKey } from '../util';
|
||||
import { SECONDARY_ROLE_BOTTOM_MARGIN } from '../../constants';
|
||||
import { getContentWidth } from '../../../../util';
|
||||
|
||||
@Component({
|
||||
selector: 'g[svg-icon-text-key]',
|
||||
@@ -21,8 +22,14 @@ export class SvgIconTextKeyComponent implements OnChanges {
|
||||
useY: number;
|
||||
textY: number;
|
||||
spanX: number;
|
||||
textWidth: number;
|
||||
secondaryTextY: number;
|
||||
secondaryHeight: number;
|
||||
fontSize: number;
|
||||
text1: string;
|
||||
text1Y: number;
|
||||
text2: string;
|
||||
text2Y: number;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
@@ -32,22 +39,120 @@ export class SvgIconTextKeyComponent implements OnChanges {
|
||||
}
|
||||
|
||||
private calculatePositions(): void {
|
||||
let textYModifier = 0;
|
||||
let secondaryYModifier = 0;
|
||||
|
||||
if (this.secondaryText && isRectangleAsSecondaryRoleKey(this.width, this.height)) {
|
||||
textYModifier = this.height / 5;
|
||||
secondaryYModifier = 5;
|
||||
}
|
||||
|
||||
const isRectangle = this.width > this.height * 1.8;
|
||||
|
||||
this.useWidth = this.width / 3;
|
||||
this.useHeight = this.height / 3;
|
||||
this.useX = (this.width > 2 * this.height) ? 0 : this.width / 3;
|
||||
this.useY = (this.width > 2 * this.height) ? this.height / 3 : this.height / 10;
|
||||
this.textY = (this.width > 2 * this.height) ? this.height / 2 : this.height * 0.6;
|
||||
this.spanX = (this.width > 2 * this.height) ? this.width * 0.6 : this.width / 2;
|
||||
|
||||
if (isRectangle) {
|
||||
this.textWidth = this.width * 0.65;
|
||||
this.useX = 0;
|
||||
this.useY = this.height / 3;
|
||||
this.spanX = this.width * 0.6;
|
||||
} else {
|
||||
this.textWidth = this.width * 0.95;
|
||||
this.useX = this.width / 3;
|
||||
this.useY = this.height / 10;
|
||||
this.spanX = this.width / 2;
|
||||
}
|
||||
|
||||
if (this.secondaryText) {
|
||||
this.secondaryHeight = this.height / 4;
|
||||
this.secondaryTextY = this.height - this.secondaryHeight - SECONDARY_ROLE_BOTTOM_MARGIN - secondaryYModifier;
|
||||
} else {
|
||||
this.secondaryHeight = 0;
|
||||
this.secondaryTextY = 0;
|
||||
}
|
||||
this.fontSize = 19;
|
||||
this.text1 = '';
|
||||
this.text2 = '';
|
||||
while (this.fontSize > 10 && !this.isFullTextVisible()) {
|
||||
this.calculateTexts(isRectangle);
|
||||
this.fontSize--;
|
||||
}
|
||||
}
|
||||
|
||||
private calculateTexts(isRectangle: boolean): void {
|
||||
if (!this.text) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.text1 = this.getText(0);
|
||||
|
||||
this.text2 = this.getText(this.text1.length);
|
||||
|
||||
const lineHeight = this.fontSize;
|
||||
const lines = this.text2 ? 1 : 0;
|
||||
|
||||
if (isRectangle) {
|
||||
const textboxHeight = this.height - this.secondaryHeight;
|
||||
this.textY = textboxHeight / 2 - 0.5 * lines * lineHeight;
|
||||
} else {
|
||||
const textboxHeight = this.height - this.secondaryHeight + this.useHeight;
|
||||
this.textY = textboxHeight / 2 - 0.5 * lines * lineHeight;
|
||||
}
|
||||
this.text1Y = 0;
|
||||
this.text2Y = this.text1Y + 1.2 * lines * lineHeight;
|
||||
}
|
||||
|
||||
private getText(startPosition: number): string {
|
||||
const style: CSSStyleDeclaration = {
|
||||
font: `${this.fontSize}px Helvetica`
|
||||
} as any;
|
||||
|
||||
let result = '';
|
||||
let lastSpacePosition = 0;
|
||||
|
||||
for (let i = startPosition; i < this.text.length; i++) {
|
||||
const char = this.text[i];
|
||||
|
||||
// skip space if result start with space
|
||||
if (char === ' ' && result === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const newText = result += char;
|
||||
const textWidth = getContentWidth(style, newText);
|
||||
|
||||
if (char === ' ') {
|
||||
lastSpacePosition = i;
|
||||
}
|
||||
|
||||
if (textWidth > this.textWidth) {
|
||||
break;
|
||||
}
|
||||
|
||||
result = newText;
|
||||
}
|
||||
|
||||
if (lastSpacePosition > 0 && lastSpacePosition < result.length) {
|
||||
result = result.substr(0, lastSpacePosition);
|
||||
} else if (this.fontSize === 11) {
|
||||
const cleanResult = result.replace(/ /g, '');
|
||||
const cleanText = this.text.substr(startPosition).replace(/ /g, '');
|
||||
if (cleanResult.length < cleanText.length) {
|
||||
result = result.substring(0, result.length - 3) + '...';
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private isFullTextVisible(): boolean {
|
||||
const visibleText = (this.text1 + this.text2).replace(/ /g, '');
|
||||
|
||||
if (this.text2.endsWith('...')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const textLength = this.text.replace(/ /g, '').length;
|
||||
|
||||
return visibleText.length === textLength;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {
|
||||
Component, ElementRef, EventEmitter, HostListener, Input, OnChanges, OnDestroy, OnInit, Output, Renderer,
|
||||
Component, ElementRef, EventEmitter, HostListener, Input, OnChanges, OnDestroy, OnInit, Output,
|
||||
SimpleChange, ChangeDetectionStrategy
|
||||
} from '@angular/core';
|
||||
import { animate, group, state, style, transition, trigger } from '@angular/animations';
|
||||
@@ -29,6 +29,7 @@ import { getMacros } from '../../../../store/reducers/user-configuration';
|
||||
import { SvgKeyCaptureEvent, SvgKeyClickEvent } from '../../../../models/svg-key-events';
|
||||
import { OperatingSystem } from '../../../../models/operating-system';
|
||||
import { KeyModifierModel } from '../../../../models/key-modifier-model';
|
||||
import { StartKeypressCapturingAction, StopKeypressCapturingAction } from '../../../../store/actions/app';
|
||||
|
||||
enum LabelTypes {
|
||||
KeystrokeKey,
|
||||
@@ -107,10 +108,9 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
|
||||
|
||||
constructor(
|
||||
private mapper: MapperService,
|
||||
store: Store<AppState>,
|
||||
private store: Store<AppState>,
|
||||
private element: ElementRef,
|
||||
private captureService: CaptureService,
|
||||
private renderer: Renderer
|
||||
private captureService: CaptureService
|
||||
) {
|
||||
this.subscription = store.let(getMacros())
|
||||
.subscribe((macros: Macro[]) => this.macros = macros);
|
||||
@@ -136,7 +136,7 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
|
||||
onMouseDown(e: MouseEvent) {
|
||||
if ((e.which === 2 || e.button === 2) && this.capturingEnabled) {
|
||||
e.preventDefault();
|
||||
this.renderer.invokeElementMethod(this.element.nativeElement, 'focus');
|
||||
this.element.nativeElement.focus();
|
||||
|
||||
if (this.recording) {
|
||||
this.reset();
|
||||
@@ -145,6 +145,7 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.recordAnimation = 'active';
|
||||
this.shiftPressed = e.shiftKey;
|
||||
this.altPressed = e.altKey;
|
||||
this.store.dispatch(new StartKeypressCapturingAction());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,6 +243,7 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.captureService.initModifiers();
|
||||
this.shiftPressed = false;
|
||||
this.altPressed = false;
|
||||
this.store.dispatch(new StopKeypressCapturingAction());
|
||||
}
|
||||
|
||||
private saveScanCode(code = 0) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<svg:use [attr.xlink:href]="icon"
|
||||
[attr.width]="svgWidth"
|
||||
[attr.height]="svgHeight"
|
||||
[attr.x]="svgWidth"
|
||||
[attr.x]="svgX"
|
||||
[attr.y]="svgHeight">
|
||||
</svg:use>
|
||||
<svg:g svg-secondary-role
|
||||
|
||||
|
Before Width: | Height: | Size: 340 B After Width: | Height: | Size: 336 B |
@@ -16,6 +16,7 @@ export class SvgSingleIconKeyComponent implements OnChanges {
|
||||
|
||||
svgHeight: number;
|
||||
svgWidth: number;
|
||||
svgX: number;
|
||||
secondaryTextY: number;
|
||||
secondaryHeight: number;
|
||||
|
||||
@@ -34,8 +35,9 @@ export class SvgSingleIconKeyComponent implements OnChanges {
|
||||
secondaryYModifier = 5;
|
||||
}
|
||||
|
||||
this.svgWidth = this.width / 3;
|
||||
this.svgWidth = this.width / 2.075;
|
||||
this.svgHeight = this.height / 3;
|
||||
this.svgX = (this.width - this.svgWidth) / 2;
|
||||
this.secondaryHeight = this.height / 4;
|
||||
this.secondaryTextY = this.height - this.secondaryHeight - SECONDARY_ROLE_BOTTOM_MARGIN - secondaryYModifier;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
OnChanges,
|
||||
OnInit,
|
||||
Output,
|
||||
Renderer,
|
||||
SimpleChanges,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
@@ -100,8 +99,7 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
|
||||
constructor(
|
||||
private store: Store<AppState>,
|
||||
private mapper: MapperService,
|
||||
private element: ElementRef,
|
||||
private renderer: Renderer
|
||||
private element: ElementRef
|
||||
) {
|
||||
this.keyEditConfig = {
|
||||
moduleId: undefined,
|
||||
@@ -216,7 +214,7 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
|
||||
this.keyPosition = this.keyElement.getBoundingClientRect();
|
||||
this.popoverInitKeyAction = keyAction;
|
||||
this.popoverShown = true;
|
||||
this.renderer.invokeElementMethod(this.popover.nativeElement, 'focus');
|
||||
this.popover.nativeElement.focus();
|
||||
}
|
||||
|
||||
showTooltip(keyAction: KeyAction, event: MouseEvent): void {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<ol>
|
||||
<li>Open a terminal.</li>
|
||||
<li>Run <code>su</code> to become root.</li>
|
||||
<li>Paste the following script:</li>
|
||||
</ol>
|
||||
|
||||
<div class="copy-container">
|
||||
<span class="fa fa-2x fa-copy"
|
||||
ngxClipboard
|
||||
[cbContent]="command"
|
||||
title="Copy to clipboard"
|
||||
data-toggle="tooltip"
|
||||
data-placement="top"></span>
|
||||
<pre><code>{{ command }}</code></pre>
|
||||
</div>
|
||||
@@ -0,0 +1,19 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'udev-rules',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
templateUrl: './udev-rules.component.html'
|
||||
})
|
||||
export class UdevRulesComponent {
|
||||
command = `cat <<EOF >/etc/udev/rules.d/50-uhk60.rules
|
||||
# Ultimate Hacking Keyboard rules
|
||||
# These are the udev rules for accessing the USB interfaces of the UHK as non-root users.
|
||||
# Copy this file to /etc/udev/rules.d and physically reconnect the UHK afterwards.
|
||||
SUBSYSTEM=="input", GROUP="input", MODE="0666"
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="612[0-7]", MODE:="0666", GROUP="plugdev"
|
||||
KERNEL=="hidraw*", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="612[0-7]", MODE="0666", GROUP="plugdev"
|
||||
EOF
|
||||
udevadm trigger
|
||||
udevadm settle`;
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
<div class="pull-right">
|
||||
<div class="alert alert-warning alert-dismissible" role="alert" [@slideInOut]="slideInOut">
|
||||
<div *ngIf="text"
|
||||
[@slideInOut]
|
||||
class="alert alert-warning alert-dismissible"
|
||||
role="alert">
|
||||
<button type="button"
|
||||
class="close"
|
||||
aria-label="Close"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
import { animate, style, transition, trigger } from '@angular/animations';
|
||||
|
||||
import { Notification } from 'uhk-common';
|
||||
|
||||
@@ -9,15 +9,16 @@ import { Notification } from 'uhk-common';
|
||||
styleUrls: ['./undoable-notifier.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
animations: [
|
||||
trigger('slideInOut', [
|
||||
state('in', style({
|
||||
transform: 'translate3d(0, 0, 0)'
|
||||
})),
|
||||
state('out', style({
|
||||
transform: 'translate3d(200%, 0, 0)'
|
||||
})),
|
||||
transition('in => out', animate('400ms ease-in-out')),
|
||||
transition('out => in', animate('400ms ease-in-out'))
|
||||
trigger(
|
||||
'slideInOut', [
|
||||
transition(':enter', [
|
||||
style({transform: 'translateX(100%)'}),
|
||||
animate('400ms ease-in-out', style({transform: 'translateX(0)'}))
|
||||
]),
|
||||
transition(':leave', [
|
||||
style({transform: 'translateX(0)'}),
|
||||
animate('400ms ease-in-out', style({transform: 'translateX(100%)'}))
|
||||
])
|
||||
])
|
||||
]
|
||||
})
|
||||
@@ -28,16 +29,14 @@ export class UndoableNotifierComponent implements OnChanges {
|
||||
@Output() close = new EventEmitter();
|
||||
@Output() undo = new EventEmitter();
|
||||
|
||||
get slideInOut() {
|
||||
return this.notification ? 'in' : 'out';
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes['notification']) {
|
||||
const not: Notification = changes['notification'].currentValue;
|
||||
if (not) {
|
||||
this.text = not.message;
|
||||
this.undoable = !!not.extra;
|
||||
} else {
|
||||
this.text = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Directive, ElementRef, HostListener, Renderer } from '@angular/core';
|
||||
import { Directive, ElementRef, HostListener, Renderer2 } from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[cancelable]'
|
||||
@@ -7,15 +7,15 @@ export class CancelableDirective {
|
||||
|
||||
private originalValue: string;
|
||||
|
||||
constructor(private elementRef: ElementRef, private renderer: Renderer) { }
|
||||
constructor(private elementRef: ElementRef, private renderer: Renderer2) { }
|
||||
|
||||
@HostListener('focus') onFocus(): void {
|
||||
this.originalValue = this.elementRef.nativeElement.value;
|
||||
}
|
||||
|
||||
@HostListener('keyup.escape') onEscape(): void {
|
||||
this.renderer.setElementProperty(this.elementRef.nativeElement, 'value', this.originalValue);
|
||||
this.renderer.invokeElementMethod(this.elementRef.nativeElement, 'blur');
|
||||
this.renderer.setProperty(this.elementRef.nativeElement, 'value', this.originalValue);
|
||||
this.elementRef.nativeElement.blur();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
4
packages/uhk-web/src/app/models/missing-device-state.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface MissingDeviceState {
|
||||
header: string;
|
||||
subtitle: string;
|
||||
}
|
||||
@@ -2,4 +2,5 @@ export interface PrivilagePageSate {
|
||||
showWhatWillThisDo: boolean;
|
||||
showWhatWillThisDoContent: boolean;
|
||||
permissionSetupFailed: boolean;
|
||||
updateUdevRules: boolean;
|
||||
}
|
||||
|
||||
@@ -175,6 +175,13 @@ export class MapperService {
|
||||
];
|
||||
}
|
||||
|
||||
public getOsSpecificModifierTextByValue(value: KeyModifiers): string {
|
||||
const keyModifier = [...this.getLeftKeyModifiers(), ...this.getRightKeyModifiers()]
|
||||
.find(modifier => modifier.value === value);
|
||||
|
||||
return (keyModifier || {text: ''}).text;
|
||||
}
|
||||
|
||||
private initOsSpecificText(): void {
|
||||
this.osSpecificTexts = new Map<string, string>();
|
||||
|
||||
@@ -305,24 +312,26 @@ export class MapperService {
|
||||
this.basicScanCodeTextMap.set(113, ['F22']);
|
||||
this.basicScanCodeTextMap.set(114, ['F23']);
|
||||
this.basicScanCodeTextMap.set(115, ['F24']);
|
||||
this.basicScanCodeTextMap.set(135, ['Int1']);
|
||||
this.basicScanCodeTextMap.set(136, ['Int2']);
|
||||
this.basicScanCodeTextMap.set(137, ['Int3']);
|
||||
this.basicScanCodeTextMap.set(144, ['Lang1']);
|
||||
this.basicScanCodeTextMap.set(145, ['Lang2']);
|
||||
this.basicScanCodeTextMap.set(176, ['00']);
|
||||
this.basicScanCodeTextMap.set(177, ['000']);
|
||||
|
||||
this.mediaScanCodeTextMap = new Map<number, string[]>();
|
||||
this.mediaScanCodeTextMap.set(138, ['WWW']);
|
||||
this.mediaScanCodeTextMap.set(176, ['Play']);
|
||||
this.mediaScanCodeTextMap.set(177, ['Pause']);
|
||||
this.mediaScanCodeTextMap.set(181, ['Next']);
|
||||
this.mediaScanCodeTextMap.set(182, ['Prev']);
|
||||
this.mediaScanCodeTextMap.set(183, ['Stop']);
|
||||
this.mediaScanCodeTextMap.set(184, ['Eject']);
|
||||
this.mediaScanCodeTextMap.set(204, ['Eject', 'Stop']);
|
||||
this.mediaScanCodeTextMap.set(205, ['Pause', 'Play']);
|
||||
this.mediaScanCodeTextMap.set(226, ['Mute']);
|
||||
this.mediaScanCodeTextMap.set(233, ['Vol +']);
|
||||
this.mediaScanCodeTextMap.set(234, ['Vol -']);
|
||||
|
||||
this.mediaScanCodeTextMap.set(406, ['Launch Web Browser']);
|
||||
this.mediaScanCodeTextMap.set(394, ['Launch Email Client']);
|
||||
this.mediaScanCodeTextMap.set(402, ['Launch Calculator']);
|
||||
|
||||
@@ -346,17 +355,16 @@ export class MapperService {
|
||||
this.basicScancodeIcons.set(101, 'icon-kbd__mod--menu');
|
||||
|
||||
this.mediaScancodeIcons = new Map<number, string>();
|
||||
this.mediaScancodeIcons.set(138, 'icon-kbd__fn--browser');
|
||||
this.mediaScancodeIcons.set(176, 'icon-kbd__media--play');
|
||||
this.mediaScancodeIcons.set(177, 'icon-kbd__media--pause');
|
||||
this.mediaScancodeIcons.set(181, 'icon-kbd__media--next');
|
||||
this.mediaScancodeIcons.set(182, 'icon-kbd__media--prev');
|
||||
this.mediaScancodeIcons.set(184, 'icon-kbd__fn--eject');
|
||||
this.mediaScancodeIcons.set(205, 'icon-kbd__play-pause');
|
||||
this.mediaScancodeIcons.set(226, 'icon-kbd__media--mute');
|
||||
this.mediaScancodeIcons.set(233, 'icon-kbd__media--vol-up');
|
||||
this.mediaScancodeIcons.set(234, 'icon-kbd__media--vol-down');
|
||||
|
||||
this.mediaScancodeIcons.set(406, 'icon-kbd__media--web-browser');
|
||||
this.mediaScancodeIcons.set(394, 'icon-kbd__media--email-client');
|
||||
this.mediaScancodeIcons.set(402, 'icon-kbd__media--calculator');
|
||||
|
||||
|
||||
@@ -111,6 +111,7 @@ import { UhkDeviceBootloaderNotActiveGuard } from './services/uhk-device-bootloa
|
||||
import { FileUploadComponent } from './components/file-upload';
|
||||
import { AutoGrowInputComponent } from './components/auto-grow-input';
|
||||
import { HelpPageComponent } from './components/agent/help-page/help-page.component';
|
||||
import { UdevRulesComponent } from './components/udev-rules/udev-rules.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -188,7 +189,8 @@ import { HelpPageComponent } from './components/agent/help-page/help-page.compon
|
||||
AutoGrowInputComponent,
|
||||
HelpPageComponent,
|
||||
ExternalUrlDirective,
|
||||
SvgSecondaryRoleComponent
|
||||
SvgSecondaryRoleComponent,
|
||||
UdevRulesComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
||||
@@ -20,7 +20,9 @@ export const ActionTypes = {
|
||||
OPEN_URL_IN_NEW_WINDOW: type(PREFIX + 'Open URL in new Window'),
|
||||
PRIVILEGE_WHAT_WILL_THIS_DO: type(PREFIX + 'What will this do clicked'),
|
||||
SETUP_PERMISSION_ERROR: type(PREFIX + 'Setup permission error'),
|
||||
LOAD_APP_START_INFO: type(PREFIX + 'Load app start info')
|
||||
LOAD_APP_START_INFO: type(PREFIX + 'Load app start info'),
|
||||
START_KEYPRESS_CAPTURING: type(PREFIX + 'Start keypress capturing'),
|
||||
STOP_KEYPRESS_CAPTURING: type(PREFIX + 'Stop keypress capturing')
|
||||
};
|
||||
|
||||
export class AppBootsrappedAction implements Action {
|
||||
@@ -103,6 +105,14 @@ export class LoadAppStartInfoAction implements Action {
|
||||
type = ActionTypes.LOAD_APP_START_INFO;
|
||||
}
|
||||
|
||||
export class StartKeypressCapturingAction implements Action {
|
||||
type = ActionTypes.START_KEYPRESS_CAPTURING;
|
||||
}
|
||||
|
||||
export class StopKeypressCapturingAction implements Action {
|
||||
type = ActionTypes.STOP_KEYPRESS_CAPTURING;
|
||||
}
|
||||
|
||||
export type Actions
|
||||
= AppStartedAction
|
||||
| AppBootsrappedAction
|
||||
@@ -118,4 +128,6 @@ export type Actions
|
||||
| PrivilegeWhatWillThisDoAction
|
||||
| SetupPermissionErrorAction
|
||||
| LoadAppStartInfoAction
|
||||
| StartKeypressCapturingAction
|
||||
| StopKeypressCapturingAction
|
||||
;
|
||||
|
||||
@@ -66,11 +66,7 @@ export class ApplicationEffects {
|
||||
this.logService.debug('[AppEffect][processStartInfo] payload:', appInfo);
|
||||
return [
|
||||
new ApplyAppStartInfoAction(appInfo),
|
||||
new ConnectionStateChangedAction({
|
||||
connected: appInfo.deviceConnected,
|
||||
hasPermission: appInfo.hasPermission,
|
||||
bootloaderActive: appInfo.bootloaderActive
|
||||
})
|
||||
new ConnectionStateChangedAction(appInfo.deviceConnectionState)
|
||||
];
|
||||
});
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
HardwareConfiguration,
|
||||
IpcResponse,
|
||||
NotificationType,
|
||||
UdevRulesInfo,
|
||||
UserConfiguration
|
||||
} from 'uhk-common';
|
||||
import {
|
||||
@@ -39,9 +40,10 @@ import {
|
||||
UpdateFirmwareSuccessAction,
|
||||
UpdateFirmwareWithAction
|
||||
} from '../actions/device';
|
||||
import { AppRendererService } from '../../services/app-renderer.service';
|
||||
import { DeviceRendererService } from '../../services/device-renderer.service';
|
||||
import { SetupPermissionErrorAction, ShowNotificationAction } from '../actions/app';
|
||||
import { AppState, getRouterState } from '../index';
|
||||
import { AppState, deviceConnected, getRouterState } from '../index';
|
||||
import {
|
||||
ActionTypes as UserConfigActions,
|
||||
ApplyUserConfigurationFromFileAction,
|
||||
@@ -57,7 +59,7 @@ export class DeviceEffects {
|
||||
@Effect()
|
||||
deviceConnectionStateChange$: Observable<Action> = this.actions$
|
||||
.ofType<ConnectionStateChangedAction>(ActionTypes.CONNECTION_STATE_CHANGED)
|
||||
.withLatestFrom(this.store.select(getRouterState))
|
||||
.withLatestFrom(this.store.select(getRouterState), this.store.select(deviceConnected))
|
||||
.do(([action, route]) => {
|
||||
const state = action.payload;
|
||||
|
||||
@@ -65,23 +67,24 @@ export class DeviceEffects {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!state.hasPermission) {
|
||||
this.router.navigate(['/privilege']);
|
||||
if (!state.hasPermission || !state.zeroInterfaceAvailable) {
|
||||
return this.router.navigate(['/privilege']);
|
||||
}
|
||||
else if (state.bootloaderActive) {
|
||||
this.router.navigate(['/recovery-device']);
|
||||
}
|
||||
else if (state.connected) {
|
||||
this.router.navigate(['/']);
|
||||
}
|
||||
else {
|
||||
this.router.navigate(['/detection']);
|
||||
}
|
||||
})
|
||||
.switchMap(([action, route]) => {
|
||||
const state = action.payload;
|
||||
|
||||
if (state.connected && state.hasPermission) {
|
||||
if (state.bootloaderActive) {
|
||||
return this.router.navigate(['/recovery-device']);
|
||||
}
|
||||
|
||||
if (state.connected && state.zeroInterfaceAvailable) {
|
||||
return this.router.navigate(['/']);
|
||||
}
|
||||
|
||||
return this.router.navigate(['/detection']);
|
||||
})
|
||||
.switchMap(([action, route, connected]) => {
|
||||
const payload = action.payload;
|
||||
|
||||
if (connected && payload.hasPermission && payload.zeroInterfaceAvailable) {
|
||||
return Observable.of(new LoadConfigFromDeviceAction());
|
||||
}
|
||||
|
||||
@@ -99,16 +102,13 @@ export class DeviceEffects {
|
||||
setPrivilegeOnLinuxReply$: Observable<Action> = this.actions$
|
||||
.ofType<SetPrivilegeOnLinuxReplyAction>(ActionTypes.SET_PRIVILEGE_ON_LINUX_REPLY)
|
||||
.map(action => action.payload)
|
||||
.map((response: any): any => {
|
||||
.switchMap((response: any): any => {
|
||||
if (response.success) {
|
||||
return new ConnectionStateChangedAction({
|
||||
connected: true,
|
||||
hasPermission: true,
|
||||
bootloaderActive: false
|
||||
});
|
||||
this.appRendererService.getAppStartInfo();
|
||||
return Observable.empty();
|
||||
}
|
||||
|
||||
return new SetupPermissionErrorAction(response.error);
|
||||
return Observable.of(new SetupPermissionErrorAction(response.error));
|
||||
});
|
||||
|
||||
@Effect({dispatch: false})
|
||||
@@ -243,6 +243,7 @@ export class DeviceEffects {
|
||||
|
||||
constructor(private actions$: Actions,
|
||||
private router: Router,
|
||||
private appRendererService: AppRendererService,
|
||||
private deviceRendererService: DeviceRendererService,
|
||||
private store: Store<AppState>,
|
||||
private dataStorageRepository: DataStorageRepositoryService,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { createSelector } from 'reselect';
|
||||
import { ActionReducerMap, MetaReducer } from '@ngrx/store';
|
||||
import { RouterReducerState, routerReducer } from '@ngrx/router-store';
|
||||
import { routerReducer, RouterReducerState } from '@ngrx/router-store';
|
||||
import { storeFreeze } from 'ngrx-store-freeze';
|
||||
import { Keymap, UserConfiguration } from 'uhk-common';
|
||||
import { HardwareModules, Keymap, UserConfiguration } from 'uhk-common';
|
||||
|
||||
import * as fromUserConfig from './reducers/user-configuration';
|
||||
import * as fromPreset from './reducers/preset';
|
||||
@@ -14,6 +14,8 @@ import * as fromSelectors from './reducers/selectors';
|
||||
import { initProgressButtonState } from './reducers/progress-button-state';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { RouterStateUrl } from './router-util';
|
||||
import { PrivilagePageSate } from '../models/privilage-page-sate';
|
||||
import { isVersionGte } from '../util';
|
||||
|
||||
// State interface for the application
|
||||
export interface AppState {
|
||||
@@ -45,15 +47,14 @@ export const getUserConfiguration = (state: AppState) => state.userConfiguration
|
||||
export const appState = (state: AppState) => state.app;
|
||||
|
||||
export const showAddonMenu = createSelector(appState, fromApp.showAddonMenu);
|
||||
export const allowLayerDoubleTap = createSelector(appState, fromApp.allowLayerDoubleTap);
|
||||
export const getUndoableNotification = createSelector(appState, fromApp.getUndoableNotification);
|
||||
export const getPrevUserConfiguration = createSelector(appState, fromApp.getPrevUserConfiguration);
|
||||
export const runningInElectron = createSelector(appState, fromApp.runningInElectron);
|
||||
export const getKeyboardLayout = createSelector(appState, fromApp.getKeyboardLayout);
|
||||
export const deviceConfigurationLoaded = createSelector(appState, fromApp.deviceConfigurationLoaded);
|
||||
export const getAgentVersionInfo = createSelector(appState, fromApp.getAgentVersionInfo);
|
||||
export const getPrivilegePageState = createSelector(appState, fromApp.getPrivilagePageState);
|
||||
export const getOperatingSystem = createSelector(appState, fromSelectors.getOperatingSystem);
|
||||
export const keypressCapturing = createSelector(appState, fromApp.keypressCapturing);
|
||||
export const runningOnNotSupportedWindows = createSelector(appState, fromApp.runningOnNotSupportedWindows);
|
||||
export const firmwareUpgradeAllowed = createSelector(runningOnNotSupportedWindows, notSupportedOs => !notSupportedOs);
|
||||
|
||||
@@ -65,14 +66,21 @@ export const getAutoUpdateSettings = createSelector(appUpdateSettingsState, auto
|
||||
export const getCheckingForUpdate = createSelector(appUpdateSettingsState, autoUpdateSettings.checkingForUpdate);
|
||||
|
||||
export const deviceState = (state: AppState) => state.device;
|
||||
export const isDeviceConnected = createSelector(deviceState, fromDevice.isDeviceConnected);
|
||||
export const deviceConnected = createSelector(runningInElectron, isDeviceConnected, (electron, connected) => {
|
||||
return !electron ? true : connected;
|
||||
});
|
||||
export const devicePermission = createSelector(deviceState, fromDevice.hasDevicePermission);
|
||||
export const hasDevicePermission = createSelector(runningInElectron, devicePermission, (electron, permission) => {
|
||||
return !electron ? true : permission;
|
||||
export const deviceConnected = createSelector(
|
||||
runningInElectron, deviceState, appState,
|
||||
(electron, device, app) => {
|
||||
if (!electron) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (app.platform === 'linux') {
|
||||
return device.connected && (device.zeroInterfaceAvailable || device.updatingFirmware);
|
||||
}
|
||||
|
||||
return device.connected;
|
||||
});
|
||||
export const hasDevicePermission = createSelector(deviceState, fromDevice.hasDevicePermission);
|
||||
export const getMissingDeviceState = createSelector(deviceState, fromDevice.getMissingDeviceState);
|
||||
export const saveToKeyboardStateSelector = createSelector(deviceState, fromDevice.getSaveToKeyboardState);
|
||||
export const saveToKeyboardState = createSelector(runningInElectron, saveToKeyboardStateSelector, (electron, state) => {
|
||||
return electron ? state : initProgressButtonState;
|
||||
@@ -87,6 +95,18 @@ export const getRestoreUserConfiguration = createSelector(deviceState, fromDevic
|
||||
export const bootloaderActive = createSelector(deviceState, fromDevice.bootloaderActive);
|
||||
export const firmwareUpgradeFailed = createSelector(deviceState, fromDevice.firmwareUpgradeFailed);
|
||||
export const firmwareUpgradeSuccess = createSelector(deviceState, fromDevice.firmwareUpgradeSuccess);
|
||||
export const getUpdateUdevRules = createSelector(deviceState, fromDevice.updateUdevRules);
|
||||
|
||||
export const getPrivilegePageState = createSelector(appState, getUpdateUdevRules, (app, updateUdevRules): PrivilagePageSate => {
|
||||
const permissionSetupFailed = !!app.permissionError;
|
||||
|
||||
return {
|
||||
permissionSetupFailed,
|
||||
updateUdevRules,
|
||||
showWhatWillThisDo: !app.privilegeWhatWillThisDoClicked && !permissionSetupFailed,
|
||||
showWhatWillThisDoContent: app.privilegeWhatWillThisDoClicked || permissionSetupFailed
|
||||
};
|
||||
});
|
||||
|
||||
export const getSideMenuPageState = createSelector(
|
||||
showAddonMenu,
|
||||
@@ -112,3 +132,13 @@ export const getSideMenuPageState = createSelector(
|
||||
);
|
||||
|
||||
export const getRouterState = (state: AppState) => state.router;
|
||||
|
||||
export const macroPlaybackSupported = createSelector(getHardwareModules, (hardwareModules: HardwareModules): boolean => {
|
||||
return isVersionGte(hardwareModules.rightModuleInfo.firmwareVersion, '8.4.3');
|
||||
});
|
||||
export const layerDoubleTapSupported = createSelector(
|
||||
getHardwareModules,
|
||||
(hardwareModules: HardwareModules): boolean => {
|
||||
return isVersionGte(hardwareModules.rightModuleInfo.firmwareVersion, '8.4.3');
|
||||
}
|
||||
);
|
||||
|
||||
@@ -32,6 +32,7 @@ export interface State {
|
||||
permissionError?: any;
|
||||
platform?: string;
|
||||
osVersion?: string;
|
||||
keypressCapturing: boolean;
|
||||
}
|
||||
|
||||
export const initialState: State = {
|
||||
@@ -41,7 +42,8 @@ export const initialState: State = {
|
||||
runningInElectron: runInElectron(),
|
||||
configLoading: true,
|
||||
agentVersionInfo: getVersions(),
|
||||
privilegeWhatWillThisDoClicked: false
|
||||
privilegeWhatWillThisDoClicked: false,
|
||||
keypressCapturing: false
|
||||
};
|
||||
|
||||
export function reducer(state = initialState, action: Action & { payload: any }) {
|
||||
@@ -151,13 +153,24 @@ export function reducer(state = initialState, action: Action & { payload: any })
|
||||
permissionError: null
|
||||
};
|
||||
|
||||
case ActionTypes.START_KEYPRESS_CAPTURING:
|
||||
return {
|
||||
...state,
|
||||
keypressCapturing: true
|
||||
};
|
||||
|
||||
case ActionTypes.STOP_KEYPRESS_CAPTURING:
|
||||
return {
|
||||
...state,
|
||||
keypressCapturing: false
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export const showAddonMenu = (state: State) => state.commandLineArgs.addons;
|
||||
export const allowLayerDoubleTap = (state: State) => state.commandLineArgs.layerDoubleTap;
|
||||
export const getUndoableNotification = (state: State) => state.undoableNotification;
|
||||
export const getPrevUserConfiguration = (state: State) => state.prevUserConfig;
|
||||
export const runningInElectron = (state: State) => state.runningInElectron;
|
||||
@@ -170,15 +183,6 @@ export const getKeyboardLayout = (state: State): KeyboardLayout => {
|
||||
};
|
||||
export const deviceConfigurationLoaded = (state: State) => !state.runningInElectron ? true : !!state.hardwareConfig;
|
||||
export const getAgentVersionInfo = (state: State) => state.agentVersionInfo || {} as VersionInformation;
|
||||
export const getPrivilagePageState = (state: State): PrivilagePageSate => {
|
||||
const permissionSetupFailed = !!state.permissionError;
|
||||
|
||||
return {
|
||||
permissionSetupFailed,
|
||||
showWhatWillThisDo: !state.privilegeWhatWillThisDoClicked && !permissionSetupFailed,
|
||||
showWhatWillThisDoContent: state.privilegeWhatWillThisDoClicked || permissionSetupFailed
|
||||
};
|
||||
};
|
||||
|
||||
export const runningOnNotSupportedWindows = (state: State): boolean => {
|
||||
if (!state.osVersion || state.platform !== 'win32') {
|
||||
@@ -191,3 +195,5 @@ export const runningOnNotSupportedWindows = (state: State): boolean => {
|
||||
|
||||
return osMajor < 6 || (osMajor === 6 && osMinor < 2);
|
||||
};
|
||||
|
||||
export const keypressCapturing = (state: State): boolean => state.keypressCapturing;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
import { HardwareModules } from 'uhk-common';
|
||||
import { HardwareModules, UdevRulesInfo } from 'uhk-common';
|
||||
|
||||
import {
|
||||
ActionTypes,
|
||||
ConnectionStateChangedAction,
|
||||
HardwareModulesLoadedAction,
|
||||
SaveConfigurationAction,
|
||||
HasBackupUserConfigurationAction,
|
||||
SaveConfigurationAction,
|
||||
UpdateFirmwareFailedAction,
|
||||
UpdateFirmwareSuccessAction
|
||||
} from '../actions/device';
|
||||
@@ -14,11 +14,14 @@ import { ActionTypes as AppActions, ElectronMainLogReceivedAction } from '../act
|
||||
import { initProgressButtonState, ProgressButtonState } from './progress-button-state';
|
||||
import { XtermCssClass, XtermLog } from '../../models/xterm-log';
|
||||
import { RestoreConfigurationState } from '../../models/restore-configuration-state';
|
||||
import { MissingDeviceState } from '../../models/missing-device-state';
|
||||
|
||||
export interface State {
|
||||
connected: boolean;
|
||||
hasPermission: boolean;
|
||||
bootloaderActive: boolean;
|
||||
zeroInterfaceAvailable: boolean;
|
||||
udevRuleInfo: UdevRulesInfo;
|
||||
saveToKeyboard: ProgressButtonState;
|
||||
savingToKeyboard: boolean;
|
||||
updatingFirmware: boolean;
|
||||
@@ -35,6 +38,8 @@ export const initialState: State = {
|
||||
connected: true,
|
||||
hasPermission: true,
|
||||
bootloaderActive: false,
|
||||
zeroInterfaceAvailable: true,
|
||||
udevRuleInfo: UdevRulesInfo.Unkonwn,
|
||||
saveToKeyboard: initProgressButtonState,
|
||||
savingToKeyboard: false,
|
||||
updatingFirmware: false,
|
||||
@@ -61,7 +66,9 @@ export function reducer(state = initialState, action: Action): State {
|
||||
...state,
|
||||
connected: data.connected,
|
||||
hasPermission: data.hasPermission,
|
||||
bootloaderActive: data.bootloaderActive
|
||||
zeroInterfaceAvailable: data.zeroInterfaceAvailable,
|
||||
bootloaderActive: data.bootloaderActive,
|
||||
udevRuleInfo: data.udevRulesInfo
|
||||
};
|
||||
}
|
||||
|
||||
@@ -222,7 +229,20 @@ export function reducer(state = initialState, action: Action): State {
|
||||
|
||||
export const updatingFirmware = (state: State) => state.updatingFirmware;
|
||||
export const isDeviceConnected = (state: State) => state.connected || state.updatingFirmware;
|
||||
export const hasDevicePermission = (state: State) => state.hasPermission;
|
||||
export const hasDevicePermission = (state: State) => state.udevRuleInfo === UdevRulesInfo.Ok;
|
||||
export const getMissingDeviceState = (state: State): MissingDeviceState => {
|
||||
if (state.connected && !state.zeroInterfaceAvailable) {
|
||||
return {
|
||||
header: 'Cannot find your UHK',
|
||||
subtitle: 'Please reconnect it!'
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
header: 'Cannot find your UHK',
|
||||
subtitle: 'Please plug it in!'
|
||||
};
|
||||
};
|
||||
export const getSaveToKeyboardState = (state: State) => state.saveToKeyboard;
|
||||
export const xtermLog = (state: State) => state.log;
|
||||
export const getHardwareModules = (state: State) => state.modules;
|
||||
@@ -236,3 +256,4 @@ export const getBackupUserConfigurationState = (state: State): RestoreConfigurat
|
||||
export const bootloaderActive = (state: State) => state.bootloaderActive;
|
||||
export const firmwareUpgradeFailed = (state: State) => state.firmwareUpdateFailed;
|
||||
export const firmwareUpgradeSuccess = (state: State) => state.firmwareUpdateSuccess;
|
||||
export const updateUdevRules = (state: State) => state.udevRuleInfo === UdevRulesInfo.Different;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import * as semver from 'semver';
|
||||
import { VersionInformation } from 'uhk-common';
|
||||
|
||||
const collectVersions = (): VersionInformation => {
|
||||
@@ -20,3 +21,11 @@ export const getVersions = (): VersionInformation => {
|
||||
}
|
||||
return versions;
|
||||
};
|
||||
|
||||
export const isVersionGte = (v1: string, v2: string): boolean => {
|
||||
if (!v1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return semver.gte(v1, v2);
|
||||
};
|
||||
|
||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 18 KiB |
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
|
||||
<path fill="#000000" d="M8 0c-4.418 0-8 3.582-8 8s3.582 8 8 8 8-3.582 8-8-3.582-8-8-8zM8 15c-0.984 0-1.92-0.203-2.769-0.57l3.643-4.098c0.081-0.092 0.126-0.21 0.126-0.332v-1.5c0-0.276-0.224-0.5-0.5-0.5-1.765 0-3.628-1.835-3.646-1.854-0.094-0.094-0.221-0.146-0.354-0.146h-2c-0.276 0-0.5 0.224-0.5 0.5v3c0 0.189 0.107 0.363 0.276 0.447l1.724 0.862v2.936c-1.813-1.265-3-3.366-3-5.745 0-1.074 0.242-2.091 0.674-3h1.826c0.133 0 0.26-0.053 0.354-0.146l2-2c0.094-0.094 0.146-0.221 0.146-0.354v-1.21c0.634-0.189 1.305-0.29 2-0.29 1.1 0 2.141 0.254 3.067 0.706-0.065 0.055-0.128 0.112-0.188 0.172-0.567 0.567-0.879 1.32-0.879 2.121s0.312 1.555 0.879 2.121c0.569 0.569 1.332 0.879 2.119 0.879 0.049 0 0.099-0.001 0.149-0.004 0.216 0.809 0.605 2.917-0.131 5.818-0.007 0.027-0.011 0.055-0.013 0.082-1.271 1.298-3.042 2.104-5.002 2.104z"></path>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
|
||||
<path fill="#000000" d="M8 0c-4.418 0-8 3.582-8 8s3.582 8 8 8 8-3.582 8-8-3.582-8-8-8zM8 15c-0.984 0-1.92-0.203-2.769-0.57l3.643-4.098c0.081-0.092 0.126-0.21 0.126-0.332v-1.5c0-0.276-0.224-0.5-0.5-0.5-1.765 0-3.628-1.835-3.646-1.854-0.094-0.094-0.221-0.146-0.354-0.146h-2c-0.276 0-0.5 0.224-0.5 0.5v3c0 0.189 0.107 0.363 0.276 0.447l1.724 0.862v2.936c-1.813-1.265-3-3.366-3-5.745 0-1.074 0.242-2.091 0.674-3h1.826c0.133 0 0.26-0.053 0.354-0.146l2-2c0.094-0.094 0.146-0.221 0.146-0.354v-1.21c0.634-0.189 1.305-0.29 2-0.29 1.1 0 2.141 0.254 3.067 0.706-0.065 0.055-0.128 0.112-0.188 0.172-0.567 0.567-0.879 1.32-0.879 2.121s0.312 1.555 0.879 2.121c0.569 0.569 1.332 0.879 2.119 0.879 0.049 0 0.099-0.001 0.149-0.004 0.216 0.809 0.605 2.917-0.131 5.818-0.007 0.027-0.011 0.055-0.013 0.082-1.271 1.298-3.042 2.104-5.002 2.104z"></path>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
98
packages/usb/package-lock.json
generated
@@ -29,9 +29,9 @@
|
||||
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
|
||||
"integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
|
||||
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
|
||||
"requires": {
|
||||
"delegates": "^1.0.0",
|
||||
"readable-stream": "^2.0.6"
|
||||
@@ -96,9 +96,9 @@
|
||||
}
|
||||
},
|
||||
"chownr": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz",
|
||||
"integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE="
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
|
||||
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g=="
|
||||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
@@ -219,9 +219,9 @@
|
||||
}
|
||||
},
|
||||
"deep-extend": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz",
|
||||
"integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8="
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
|
||||
},
|
||||
"delegates": {
|
||||
"version": "1.0.0",
|
||||
@@ -253,9 +253,9 @@
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
|
||||
},
|
||||
"expand-template": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.0.tgz",
|
||||
"integrity": "sha512-kkjwkMqj0h4w/sb32ERCDxCQkREMCAgS39DscDnSwDsbxnwwM1BTZySdC3Bn1lhY7vL08n9GoO/fVTynjDgRyQ=="
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.1.tgz",
|
||||
"integrity": "sha512-cebqLtV8KOZfw0UI8TEFWxtczxxC1jvyUvx6H4fyp1K1FN7A4Q+uggVUlOsI1K8AGU0rwOGqP8nCapdrw8CYQg=="
|
||||
},
|
||||
"fd-slicer": {
|
||||
"version": "1.0.1",
|
||||
@@ -405,9 +405,9 @@
|
||||
}
|
||||
},
|
||||
"mimic-response": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.0.tgz",
|
||||
"integrity": "sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4="
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
|
||||
"integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
@@ -424,7 +424,7 @@
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
@@ -432,32 +432,32 @@
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
||||
}
|
||||
}
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
|
||||
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA=="
|
||||
"version": "2.11.1",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz",
|
||||
"integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA=="
|
||||
},
|
||||
"node-abi": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.3.0.tgz",
|
||||
"integrity": "sha512-zwm6vU3SsVgw3e9fu48JBaRBCJGIvAgysDsqtf5+vEexFE71bEOtaMWb5zr/zODZNzTPtQlqUUpC79k68Hspow==",
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.4.5.tgz",
|
||||
"integrity": "sha512-aa/UC6Nr3+tqhHGRsAuw/edz7/q9nnetBrKWxj6rpTtm+0X9T1qU7lIEHMS3yN9JwAbRiKUbRRFy1PLz/y3aaA==",
|
||||
"requires": {
|
||||
"semver": "^5.4.1"
|
||||
}
|
||||
},
|
||||
"node-hid": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/node-hid/-/node-hid-0.5.7.tgz",
|
||||
"integrity": "sha512-dwwpOetL2+MGYgivbO22ML+45ieCGbueWv1rYxRgBoEc2QMp6UF6ZucEkYts1IA3YPWJNkmpGh6dqQ85n19szw==",
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/node-hid/-/node-hid-0.7.3.tgz",
|
||||
"integrity": "sha512-LOCqWqcOlng+Kn1Qj/54zrPVfCagg1O7RlSgMmugykBcoYvUud6BswTrJM2aXuBac+bCCm3lA2srRG8YfmyXZQ==",
|
||||
"requires": {
|
||||
"bindings": "^1.3.0",
|
||||
"nan": "^2.6.2",
|
||||
"prebuild-install": "^2.2.2"
|
||||
"nan": "^2.10.0",
|
||||
"prebuild-install": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"noop-logger": {
|
||||
@@ -538,9 +538,9 @@
|
||||
}
|
||||
},
|
||||
"prebuild-install": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.5.1.tgz",
|
||||
"integrity": "sha512-3DX9L6pzwc1m1ksMkW3Ky2WLgPQUBiySOfXVl3WZyAeJSyJb4wtoH9OmeRGcubAWsMlLiL8BTHbwfm/jPQE9Ag==",
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-4.0.0.tgz",
|
||||
"integrity": "sha512-7tayxeYboJX0RbVzdnKyGl2vhQRWr6qfClEXDhOkXjuaOKCw2q8aiuFhONRYVsG/czia7KhpykIlI2S2VaPunA==",
|
||||
"requires": {
|
||||
"detect-libc": "^1.0.3",
|
||||
"expand-template": "^1.0.2",
|
||||
@@ -574,11 +574,11 @@
|
||||
}
|
||||
},
|
||||
"rc": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.6.tgz",
|
||||
"integrity": "sha1-6xiYnG1PTxYsOZ953dKfODVWgJI=",
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
|
||||
"requires": {
|
||||
"deep-extend": "~0.4.0",
|
||||
"deep-extend": "^0.6.0",
|
||||
"ini": "~1.3.0",
|
||||
"minimist": "^1.2.0",
|
||||
"strip-json-comments": "~2.0.1"
|
||||
@@ -638,9 +638,9 @@
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
|
||||
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
|
||||
"version": "5.5.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
|
||||
"integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw=="
|
||||
},
|
||||
"set-blocking": {
|
||||
"version": "2.0.0",
|
||||
@@ -679,9 +679,9 @@
|
||||
"integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
|
||||
},
|
||||
"simple-get": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.7.0.tgz",
|
||||
"integrity": "sha512-RkE9rGPHcxYZ/baYmgJtOSM63vH0Vyq+ma5TijBcLla41SWlh8t6XYIGMR/oeZcmr+/G8k+zrClkkVrtnQ0esg==",
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz",
|
||||
"integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==",
|
||||
"requires": {
|
||||
"decompress-response": "^3.3.0",
|
||||
"once": "^1.3.1",
|
||||
@@ -708,7 +708,7 @@
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
@@ -736,9 +736,9 @@
|
||||
}
|
||||
},
|
||||
"tar-fs": {
|
||||
"version": "1.16.0",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.0.tgz",
|
||||
"integrity": "sha512-I9rb6v7mjWLtOfCau9eH5L7sLJyU2BnxtEZRQ5Mt+eRKmf1F0ohXmT/Jc3fr52kDvjJ/HV5MH3soQfPL5bQ0Yg==",
|
||||
"version": "1.16.3",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz",
|
||||
"integrity": "sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==",
|
||||
"requires": {
|
||||
"chownr": "^1.0.1",
|
||||
"mkdirp": "^0.5.1",
|
||||
@@ -815,11 +815,11 @@
|
||||
"integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
|
||||
},
|
||||
"wide-align": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
|
||||
"integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
||||
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
|
||||
"requires": {
|
||||
"string-width": "^1.0.2"
|
||||
"string-width": "^1.0.2 || 2"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"commander": "^2.11.0",
|
||||
"decompress": "^4.2.0",
|
||||
"decompress-tarbz2": "^4.1.1",
|
||||
"node-hid": "0.5.7",
|
||||
"node-hid": "0.7.3",
|
||||
"shelljs": "^0.7.8",
|
||||
"tmp": "0.0.33",
|
||||
"uhk-common": "1.0.0",
|
||||
|
||||
@@ -476,6 +476,7 @@ uhk = exports = module.exports = moduleExports = {
|
||||
testUsbStack: 1,
|
||||
debounceTimePress: 2,
|
||||
debounceTimeRelease: 3,
|
||||
usbReportSemaphore: 4,
|
||||
},
|
||||
vendorId: 0x1D50,
|
||||
devicePropertyIds: {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# Ultimate Hacking Keyboard rules
|
||||
# These are the udev rules for accessing the USB interfaces of the UHK as non-root users.
|
||||
# Copy this file to /etc/udev/rules.d and physically reconnect the UHK afterwards.
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="612[0-7]", MODE:="0666"
|
||||
SUBSYSTEM=="input", GROUP="input", MODE="0666"
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="612[0-7]", MODE:="0666", GROUP="plugdev"
|
||||
KERNEL=="hidraw*", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="612[0-7]", MODE="0666", GROUP="plugdev"
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
const fse = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
fse.copy(
|
||||
path.join(__dirname, '../packages/usb/blhost'),
|
||||
path.join(__dirname, '../tmp/packages/blhost'),
|
||||
{
|
||||
overwrite:true,
|
||||
recursive:true
|
||||
});
|
||||
27
scripts/copy-to-tmp-folder.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const fse = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
const copyOptions = {
|
||||
overwrite: true,
|
||||
recursive: true
|
||||
};
|
||||
|
||||
const promises = [];
|
||||
|
||||
promises.push(
|
||||
fse.copy(
|
||||
path.join(__dirname, '../packages/usb/blhost'),
|
||||
path.join(__dirname, '../tmp/packages/blhost'),
|
||||
copyOptions)
|
||||
);
|
||||
|
||||
promises.push(
|
||||
fse.copy(
|
||||
path.join(__dirname, '../rules'),
|
||||
path.join(__dirname, '../tmp/rules'),
|
||||
copyOptions)
|
||||
);
|
||||
|
||||
Promise
|
||||
.all(promises)
|
||||
.catch(console.error);
|
||||
@@ -102,14 +102,6 @@ if (TEST_BUILD || gitTag) {
|
||||
builder.build({
|
||||
dir: DIR,
|
||||
targets: target,
|
||||
appMetadata: {
|
||||
main: 'electron-main.js',
|
||||
name: 'UHK Agent',
|
||||
author: {
|
||||
name: 'Ultimate Gadget Laboratories'
|
||||
},
|
||||
version: rootJson.version
|
||||
},
|
||||
config: {
|
||||
directories: {
|
||||
app: electron_build_folder
|
||||
@@ -124,6 +116,7 @@ if (TEST_BUILD || gitTag) {
|
||||
},
|
||||
win: {
|
||||
extraResources,
|
||||
publisherName: 'Ultimate Gadget Laboratories Kft.',
|
||||
certificateFile: path.join(__dirname, 'certs/windows-cert.p12')
|
||||
},
|
||||
linux: {
|
||||
|
||||