From 3c40fcc793e318fb2a07ca99ae12b78531d39547 Mon Sep 17 00:00:00 2001 From: lkomanetz Date: Fri, 8 May 2020 09:45:46 -0500 Subject: [PATCH 01/15] Renamed Misc folder to BuildScripts. --- {Misc => BuildScripts}/Linux/build.sh | 0 {Misc => BuildScripts}/Windows/build.bat | 0 {Misc => BuildScripts}/Windows/startup.bat | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {Misc => BuildScripts}/Linux/build.sh (100%) mode change 100755 => 100644 rename {Misc => BuildScripts}/Windows/build.bat (100%) rename {Misc => BuildScripts}/Windows/startup.bat (100%) diff --git a/Misc/Linux/build.sh b/BuildScripts/Linux/build.sh old mode 100755 new mode 100644 similarity index 100% rename from Misc/Linux/build.sh rename to BuildScripts/Linux/build.sh diff --git a/Misc/Windows/build.bat b/BuildScripts/Windows/build.bat similarity index 100% rename from Misc/Windows/build.bat rename to BuildScripts/Windows/build.bat diff --git a/Misc/Windows/startup.bat b/BuildScripts/Windows/startup.bat similarity index 100% rename from Misc/Windows/startup.bat rename to BuildScripts/Windows/startup.bat From ca499a99257b7c2c5b3c6b32b2b2a78a65a82ce4 Mon Sep 17 00:00:00 2001 From: lkomanetz Date: Sun, 10 May 2020 11:15:46 -0500 Subject: [PATCH 02/15] Creates Electron app to maintain keybinding files used by the service. --- .gitignore | 3 + UI/XKey/converters.js | 11 + UI/XKey/file-system.js | 34 ++ UI/XKey/key-binding.js | 27 ++ UI/XKey/main-view-model.js | 28 ++ UI/XKey/main.html | 36 ++ UI/XKey/main.js | 15 + UI/XKey/package-lock.json | 650 +++++++++++++++++++++++++++++++++++++ UI/XKey/package.json | 17 + 9 files changed, 821 insertions(+) create mode 100644 UI/XKey/converters.js create mode 100644 UI/XKey/file-system.js create mode 100644 UI/XKey/key-binding.js create mode 100644 UI/XKey/main-view-model.js create mode 100644 UI/XKey/main.html create mode 100644 UI/XKey/main.js create mode 100644 UI/XKey/package-lock.json create mode 100644 UI/XKey/package.json diff --git a/.gitignore b/.gitignore index bb16d1c..548a2fd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # Build directory [Bb]uild/ +# Node Modules +node_modules/ + # Compiled Object files *.slo *.lo diff --git a/UI/XKey/converters.js b/UI/XKey/converters.js new file mode 100644 index 0000000..8960577 --- /dev/null +++ b/UI/XKey/converters.js @@ -0,0 +1,11 @@ +const { GamepadButtons } = require("./key-binding"); + +class GamepadButtonConverter { + + convert(buttonId) { + return Object.keys(GamepadButtons).find(k => GamepadButtons[k] === buttonId); + } + +} + +module.exports = { GamepadButtonConverter }; \ No newline at end of file diff --git a/UI/XKey/file-system.js b/UI/XKey/file-system.js new file mode 100644 index 0000000..13a559d --- /dev/null +++ b/UI/XKey/file-system.js @@ -0,0 +1,34 @@ +const fs = require("fs"); +const { dialog } = require("electron").remote; +const { KeyBinding, GamepadButtons } = require("./key-binding"); +const { GamepadButtonConverter } = require("./converters"); + +class FileSystem { + + constructor(buttonConverter, encodingStyle) { + this.buttonConverter = buttonConverter; + this.encodingStyle = encodingStyle; + } + + async loadBindings(location) { + const result = await dialog.showOpenDialog({ + properties: ["openFile"] + }); + + if (result.filePaths.length === 0) return []; + let bindings = []; + let data = fs.readFileSync(result.filePaths[0], this.encodingStyle).split("\n"); + + data.forEach(item => { + let buttonInfo = item.split("="); + let button = this.buttonConverter.convert(parseInt(buttonInfo[0])); + let mappedKey = buttonInfo[1]; + bindings.push(new KeyBinding(button, mappedKey)); + }); + + return bindings; + } + +} + +module.exports = FileSystem; \ No newline at end of file diff --git a/UI/XKey/key-binding.js b/UI/XKey/key-binding.js new file mode 100644 index 0000000..ba69fd1 --- /dev/null +++ b/UI/XKey/key-binding.js @@ -0,0 +1,27 @@ +class KeyBinding { + + constructor(gamepadButton, keyboardButton) { + this.gamepadButton = gamepadButton; + this.keyboardButton = keyboardButton; + } +} + +const GamepadButtons = Object.freeze({ + A: 0, + B: 1, + X: 2, + Y: 3, + LeftShoulder: 4, + RightShoulder: 5, + Back: 6, + Menu: 7, + Home: 8, + LeftStick: 9, + RightStick: 10, + DpadRight: 11, + DpadLeft: 12, + DpadUp: 13, + DpadDown: 14 +}); + +module.exports = { KeyBinding, GamepadButtons }; \ No newline at end of file diff --git a/UI/XKey/main-view-model.js b/UI/XKey/main-view-model.js new file mode 100644 index 0000000..2579af4 --- /dev/null +++ b/UI/XKey/main-view-model.js @@ -0,0 +1,28 @@ +const ko = require("knockout"); +const FileSystem = require("./file-system"); +const { GamepadButtons, KeyBinding } = require("./key-binding"); +const { GamepadButtonConverter } = require("./converters"); + +class MainWindowViewModel { + + constructor(fileSystem) { + this.fileSystem = fileSystem; + this.bindings = ko.observableArray([]); + } + + async open() { + const bindings = await this.fileSystem.loadBindings(); + this.bindings(bindings); + } + + createBindings() { + let bindings = []; + for (const prop in GamepadButtons) { + bindings.push(new KeyBinding(prop, "NULL")); + } + this.bindings(bindings); + } + +} + +ko.applyBindings(new MainWindowViewModel(new FileSystem(new GamepadButtonConverter(), "utf8"))); \ No newline at end of file diff --git a/UI/XKey/main.html b/UI/XKey/main.html new file mode 100644 index 0000000..d2f1c49 --- /dev/null +++ b/UI/XKey/main.html @@ -0,0 +1,36 @@ + + + + + + + +
+ + + + + + + + + + + + + +
Gamepad ButtonKeyboard Button
+
+ + + \ No newline at end of file diff --git a/UI/XKey/main.js b/UI/XKey/main.js new file mode 100644 index 0000000..a9c9761 --- /dev/null +++ b/UI/XKey/main.js @@ -0,0 +1,15 @@ +const { app, BrowserWindow } = require("electron"); +const { ko } = require("knockout"); + +function createWindow() { + const window = new BrowserWindow({ + width: 800, + height: 600, + webPreferences: { + nodeIntegration: true + } + }); + window.loadFile("main.html"); +} + +app.whenReady().then(createWindow); \ No newline at end of file diff --git a/UI/XKey/package-lock.json b/UI/XKey/package-lock.json new file mode 100644 index 0000000..808a699 --- /dev/null +++ b/UI/XKey/package-lock.json @@ -0,0 +1,650 @@ +{ + "name": "xkey", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@electron/get": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.12.2.tgz", + "integrity": "sha512-vAuHUbfvBQpYTJ5wB7uVIDq5c/Ry0fiTBMs7lnEYAo/qXXppIVcWdfBr57u6eRnKdVso7KSiH6p/LbQAG6Izrg==", + "requires": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "global-agent": "^2.0.2", + "global-tunnel-ng": "^2.7.1", + "got": "^9.6.0", + "progress": "^2.0.3", + "sanitize-filename": "^1.6.2", + "sumchecker": "^3.0.1" + } + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@types/node": { + "version": "12.12.38", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.38.tgz", + "integrity": "sha512-75eLjX0pFuTcUXnnWmALMzzkYorjND0ezNEycaKesbUBg9eGZp4GHPuDmkRc4mQQvIpe29zrzATNRA6hkYqwmA==" + }, + "boolean": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.1.tgz", + "integrity": "sha512-HRZPIjPcbwAVQvOTxR4YE3o8Xs98NqbbL1iEZDCz7CL8ql0Lt5iOyJFxfnAB0oFs8Oh02F/lLlg30Mexv46LjA==", + "optional": true + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + } + } + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "config-chain": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", + "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", + "optional": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "core-js": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", + "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==", + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "optional": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", + "optional": true + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "electron": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/electron/-/electron-8.2.5.tgz", + "integrity": "sha512-LxSCUwmlfJtRwthd3ofpYaZ+1C2hQSW8Ep1DD9K3VbnDItO+kb3t1z35daJgAab78j54aOwo9gMxJtvU0Ftj6w==", + "requires": { + "@electron/get": "^1.0.1", + "@types/node": "^12.0.12", + "extract-zip": "^1.0.3" + } + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "optional": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "env-paths": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", + "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==" + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "optional": true + }, + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "optional": true + }, + "extract-zip": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", + "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", + "requires": { + "concat-stream": "^1.6.2", + "debug": "^2.6.9", + "mkdirp": "^0.5.4", + "yauzl": "^2.10.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "requires": { + "pend": "~1.2.0" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "global-agent": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.1.8.tgz", + "integrity": "sha512-VpBe/rhY6Rw2VDOTszAMNambg+4Qv8j0yiTNDYEXXXxkUNGWLHp8A3ztK4YDBbFNcWF4rgsec6/5gPyryya/+A==", + "optional": true, + "requires": { + "boolean": "^3.0.0", + "core-js": "^3.6.4", + "es6-error": "^4.1.1", + "matcher": "^2.1.0", + "roarr": "^2.15.2", + "semver": "^7.1.2", + "serialize-error": "^5.0.0" + } + }, + "global-tunnel-ng": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz", + "integrity": "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==", + "optional": true, + "requires": { + "encodeurl": "^1.0.2", + "lodash": "^4.17.10", + "npm-conf": "^1.1.3", + "tunnel": "^0.0.6" + } + }, + "globalthis": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.1.tgz", + "integrity": "sha512-mJPRTc/P39NH/iNG4mXa9aIhNymaQikTrnspeCa2ZuJ+mH2QN/rXwtX3XwKrHqWgUQFbNZKtHM105aHzJalElw==", + "optional": true, + "requires": { + "define-properties": "^1.1.3" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "optional": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "optional": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "requires": { + "json-buffer": "3.0.0" + } + }, + "knockout": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/knockout/-/knockout-3.5.1.tgz", + "integrity": "sha512-wRJ9I4az0QcsH7A4v4l0enUpkS++MBx0BnL/68KaLzJg7x1qmbjSlwEoCNol7KTYZ+pmtI7Eh2J0Nu6/2Z5J/Q==" + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "optional": true + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, + "matcher": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-2.1.0.tgz", + "integrity": "sha512-o+nZr+vtJtgPNklyeUKkkH42OsK8WAfdgaJE2FNxcjLPg+5QbeEoT6vRj8Xq/iv18JlQ9cmKsEu0b94ixWf1YQ==", + "optional": true, + "requires": { + "escape-string-regexp": "^2.0.0" + } + }, + "mimic-response": { + "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.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==" + }, + "npm-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "optional": true, + "requires": { + "config-chain": "^1.1.11", + "pify": "^3.0.0" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "optional": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "optional": true + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "optional": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "roarr": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.3.tgz", + "integrity": "sha512-AEjYvmAhlyxOeB9OqPUzQCo3kuAkNfuDk/HqWbZdFsqDFpapkTjiw+p4svNEoRLvuqNTxqfL+s+gtD4eDgZ+CA==", + "optional": true, + "requires": { + "boolean": "^3.0.0", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "requires": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "optional": true + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "optional": true + }, + "serialize-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-5.0.0.tgz", + "integrity": "sha512-/VtpuyzYf82mHYTtI4QKtwHa79vAdU5OQpNPAmE/0UDdlGT0ZxHwC+J6gXkw29wwoVI8fMPsfcVHOwXtUQYYQA==", + "optional": true, + "requires": { + "type-fest": "^0.8.0" + } + }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "optional": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "requires": { + "debug": "^4.1.0" + } + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + }, + "truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=", + "requires": { + "utf8-byte-length": "^1.0.1" + } + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "optional": true + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "optional": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + }, + "utf8-byte-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", + "integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/UI/XKey/package.json b/UI/XKey/package.json new file mode 100644 index 0000000..15b934b --- /dev/null +++ b/UI/XKey/package.json @@ -0,0 +1,17 @@ +{ + "name": "xkey", + "version": "1.0.0", + "description": "UI for maintaining keybindings used by the service", + "main": "main.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "electron ." + }, + "author": "Logan Komanetz", + "license": "MIT", + "dependencies": { + "electron": "^8.2.5", + "knockout": "^3.5.1" + } +} From 151829a75121fedc6032df4e370987dcae86b010 Mon Sep 17 00:00:00 2001 From: lkomanetz Date: Sat, 16 May 2020 08:48:06 -0500 Subject: [PATCH 03/15] Switching from vanilla JS to TypeScript. --- .gitignore | 1 + UI/XKey/app.ts | 4 + UI/XKey/converters.js | 11 - UI/XKey/converters.ts | 11 + UI/XKey/file-system.js | 34 -- UI/XKey/file-system.ts | 29 ++ UI/XKey/key-binding.js | 27 -- UI/XKey/key-binding.ts | 29 ++ UI/XKey/keyboard/keyboard-button-interface.ts | 6 + UI/XKey/keyboard/windows-keyboard.ts | 32 ++ UI/XKey/main-view-model.js | 28 -- UI/XKey/main-view-model.ts | 49 +++ UI/XKey/main.html | 9 +- UI/XKey/main.js | 15 - UI/XKey/main.ts | 35 ++ UI/XKey/package-lock.json | 342 ++++++++++++++++++ UI/XKey/package.json | 7 +- UI/XKey/tsconfig.json | 17 + 18 files changed, 568 insertions(+), 118 deletions(-) create mode 100644 UI/XKey/app.ts delete mode 100644 UI/XKey/converters.js create mode 100644 UI/XKey/converters.ts delete mode 100644 UI/XKey/file-system.js create mode 100644 UI/XKey/file-system.ts delete mode 100644 UI/XKey/key-binding.js create mode 100644 UI/XKey/key-binding.ts create mode 100644 UI/XKey/keyboard/keyboard-button-interface.ts create mode 100644 UI/XKey/keyboard/windows-keyboard.ts delete mode 100644 UI/XKey/main-view-model.js create mode 100644 UI/XKey/main-view-model.ts delete mode 100644 UI/XKey/main.js create mode 100644 UI/XKey/main.ts create mode 100644 UI/XKey/tsconfig.json diff --git a/.gitignore b/.gitignore index 548a2fd..2488b76 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Build directory [Bb]uild/ +[Bb]in/ # Node Modules node_modules/ diff --git a/UI/XKey/app.ts b/UI/XKey/app.ts new file mode 100644 index 0000000..2794941 --- /dev/null +++ b/UI/XKey/app.ts @@ -0,0 +1,4 @@ +import { app, BrowserWindow } from "electron"; +import Main from "./main"; + +Main.main(app, BrowserWindow); \ No newline at end of file diff --git a/UI/XKey/converters.js b/UI/XKey/converters.js deleted file mode 100644 index 8960577..0000000 --- a/UI/XKey/converters.js +++ /dev/null @@ -1,11 +0,0 @@ -const { GamepadButtons } = require("./key-binding"); - -class GamepadButtonConverter { - - convert(buttonId) { - return Object.keys(GamepadButtons).find(k => GamepadButtons[k] === buttonId); - } - -} - -module.exports = { GamepadButtonConverter }; \ No newline at end of file diff --git a/UI/XKey/converters.ts b/UI/XKey/converters.ts new file mode 100644 index 0000000..9afec0b --- /dev/null +++ b/UI/XKey/converters.ts @@ -0,0 +1,11 @@ +import { GamepadButtons } from "./key-binding"; + +export class GamepadButtonConverter { + + convert(buttonId) { + return Object.keys(GamepadButtons).find(k => GamepadButtons[k] === buttonId); + } + +} + +// module.exports = { GamepadButtonConverter }; \ No newline at end of file diff --git a/UI/XKey/file-system.js b/UI/XKey/file-system.js deleted file mode 100644 index 13a559d..0000000 --- a/UI/XKey/file-system.js +++ /dev/null @@ -1,34 +0,0 @@ -const fs = require("fs"); -const { dialog } = require("electron").remote; -const { KeyBinding, GamepadButtons } = require("./key-binding"); -const { GamepadButtonConverter } = require("./converters"); - -class FileSystem { - - constructor(buttonConverter, encodingStyle) { - this.buttonConverter = buttonConverter; - this.encodingStyle = encodingStyle; - } - - async loadBindings(location) { - const result = await dialog.showOpenDialog({ - properties: ["openFile"] - }); - - if (result.filePaths.length === 0) return []; - let bindings = []; - let data = fs.readFileSync(result.filePaths[0], this.encodingStyle).split("\n"); - - data.forEach(item => { - let buttonInfo = item.split("="); - let button = this.buttonConverter.convert(parseInt(buttonInfo[0])); - let mappedKey = buttonInfo[1]; - bindings.push(new KeyBinding(button, mappedKey)); - }); - - return bindings; - } - -} - -module.exports = FileSystem; \ No newline at end of file diff --git a/UI/XKey/file-system.ts b/UI/XKey/file-system.ts new file mode 100644 index 0000000..4d5be4a --- /dev/null +++ b/UI/XKey/file-system.ts @@ -0,0 +1,29 @@ +import * as fs from "fs"; +import { KeyBinding, GamepadButtons } from "./key-binding"; +import { GamepadButtonConverter } from "./converters"; + +export class FileSystem { + + private buttonConverter: GamepadButtonConverter; + private encodingStyle: string; + + constructor(buttonConverter: GamepadButtonConverter, encodingStyle: string) { + this.buttonConverter = buttonConverter; + this.encodingStyle = encodingStyle; + } + + async loadBindings(location: string): Promise> { + let bindings: Array = []; + let data = fs.readFileSync(location, this.encodingStyle).split("\n"); + + data.forEach(item => { + let buttonInfo = item.split("="); + let button = this.buttonConverter.convert(parseInt(buttonInfo[0])); + let mappedKey = buttonInfo[1]; + bindings.push(new KeyBinding(button, mappedKey)); + }); + + return bindings; + } + +} \ No newline at end of file diff --git a/UI/XKey/key-binding.js b/UI/XKey/key-binding.js deleted file mode 100644 index ba69fd1..0000000 --- a/UI/XKey/key-binding.js +++ /dev/null @@ -1,27 +0,0 @@ -class KeyBinding { - - constructor(gamepadButton, keyboardButton) { - this.gamepadButton = gamepadButton; - this.keyboardButton = keyboardButton; - } -} - -const GamepadButtons = Object.freeze({ - A: 0, - B: 1, - X: 2, - Y: 3, - LeftShoulder: 4, - RightShoulder: 5, - Back: 6, - Menu: 7, - Home: 8, - LeftStick: 9, - RightStick: 10, - DpadRight: 11, - DpadLeft: 12, - DpadUp: 13, - DpadDown: 14 -}); - -module.exports = { KeyBinding, GamepadButtons }; \ No newline at end of file diff --git a/UI/XKey/key-binding.ts b/UI/XKey/key-binding.ts new file mode 100644 index 0000000..3289eef --- /dev/null +++ b/UI/XKey/key-binding.ts @@ -0,0 +1,29 @@ +export class KeyBinding { + + gamepadButton: GamepadButtons; + keyboardButton: any; + + constructor(gamepadButton, keyboardButton) { + this.gamepadButton = gamepadButton; + this.keyboardButton = keyboardButton; + } +} + +export enum GamepadButtons { + A = 0, + B = 1, + X = 2, + Y = 3, + LeftShoulder = 4, + RightShoulder = 5, + Back = 6, + Menu = 7, + Home = 8, + LeftStick = 9, + RightStick = 10, + DpadRight = 11, + DpadLeft = 12, + DpadUp = 13, + DpadDown = 14 +} +// module.exports = { KeyBinding, GamepadButtons }; \ No newline at end of file diff --git a/UI/XKey/keyboard/keyboard-button-interface.ts b/UI/XKey/keyboard/keyboard-button-interface.ts new file mode 100644 index 0000000..4329a28 --- /dev/null +++ b/UI/XKey/keyboard/keyboard-button-interface.ts @@ -0,0 +1,6 @@ +export interface IKeyboardButton { + + getKeyName(keyCode: string): string; + toKeyCode(keyName: string): string; + +} \ No newline at end of file diff --git a/UI/XKey/keyboard/windows-keyboard.ts b/UI/XKey/keyboard/windows-keyboard.ts new file mode 100644 index 0000000..b284eba --- /dev/null +++ b/UI/XKey/keyboard/windows-keyboard.ts @@ -0,0 +1,32 @@ +import { IKeyboardButton } from "./keyboard-button-interface"; + +export class WindowsKeyboard implements IKeyboardButton { + + private _map: Map; + + constructor() { + this._map = this.buildMap(); + } + + getKeyName(keyCode: string): string { + return [...this._map].find(([k, v]) => v === keyCode)[0]; + } + + toKeyCode(keyName: string): string { + return this._map.get(keyName); + } + + private buildMap(): Map { + const keyMap = new Map(); + keyMap.set("Z", "0x5a"); + keyMap.set("X", "0x58"); + keyMap.set("Spacebar", "0x20"); + keyMap.set("Enter", "0x0D"); + keyMap.set("Left Arrow", "0x25"); + keyMap.set("Up Arrow", "0x26"); + keyMap.set("Right Arrow", "0x27"); + keyMap.set("Down Arrow", "0x28"); + return keyMap; + } + +} \ No newline at end of file diff --git a/UI/XKey/main-view-model.js b/UI/XKey/main-view-model.js deleted file mode 100644 index 2579af4..0000000 --- a/UI/XKey/main-view-model.js +++ /dev/null @@ -1,28 +0,0 @@ -const ko = require("knockout"); -const FileSystem = require("./file-system"); -const { GamepadButtons, KeyBinding } = require("./key-binding"); -const { GamepadButtonConverter } = require("./converters"); - -class MainWindowViewModel { - - constructor(fileSystem) { - this.fileSystem = fileSystem; - this.bindings = ko.observableArray([]); - } - - async open() { - const bindings = await this.fileSystem.loadBindings(); - this.bindings(bindings); - } - - createBindings() { - let bindings = []; - for (const prop in GamepadButtons) { - bindings.push(new KeyBinding(prop, "NULL")); - } - this.bindings(bindings); - } - -} - -ko.applyBindings(new MainWindowViewModel(new FileSystem(new GamepadButtonConverter(), "utf8"))); \ No newline at end of file diff --git a/UI/XKey/main-view-model.ts b/UI/XKey/main-view-model.ts new file mode 100644 index 0000000..24d278d --- /dev/null +++ b/UI/XKey/main-view-model.ts @@ -0,0 +1,49 @@ +import * as ko from "knockout"; +import { remote } from "electron"; +import { FileSystem } from "./file-system"; +import { GamepadButtons, KeyBinding} from "./key-binding"; +import { GamepadButtonConverter } from "./converters"; + +const dialog = remote.dialog; + +class MainWindowViewModel { + + private fileSystem: FileSystem; + private bindings: any; + private _actionName: any; + + constructor(fileSystem: FileSystem) { + this.fileSystem = fileSystem; + this.bindings = ko.observableArray([]); + this._actionName = ko.observable(""); + } + + get actionName(): any { return this._actionName(); } + set actionName(action: any) { this._actionName(action); } + + async open(): Promise { + const filePath = await this.getFilePath(); + const bindings = await this.fileSystem.loadBindings(filePath); + this.actionName = "update-bindings-template"; + this.bindings(bindings); + } + + private async getFilePath(): Promise { + const result = await dialog.showOpenDialog({ + properties: ["openFile"] + }); + return result.filePaths.length == 1 ? result.filePaths[0] : ""; + } + + createBindings() { + let bindings = []; + for (const prop in GamepadButtons) { + bindings.push(new KeyBinding(prop, "NULL")); + } + this.actionName = "new-bindings-template"; + this.bindings(bindings); + } + +} + +ko.applyBindings(new MainWindowViewModel(new FileSystem(new GamepadButtonConverter(), "utf8"))); \ No newline at end of file diff --git a/UI/XKey/main.html b/UI/XKey/main.html index d2f1c49..1b0a109 100644 --- a/UI/XKey/main.html +++ b/UI/XKey/main.html @@ -30,7 +30,14 @@ +
- + + + \ No newline at end of file diff --git a/UI/XKey/main.js b/UI/XKey/main.js deleted file mode 100644 index a9c9761..0000000 --- a/UI/XKey/main.js +++ /dev/null @@ -1,15 +0,0 @@ -const { app, BrowserWindow } = require("electron"); -const { ko } = require("knockout"); - -function createWindow() { - const window = new BrowserWindow({ - width: 800, - height: 600, - webPreferences: { - nodeIntegration: true - } - }); - window.loadFile("main.html"); -} - -app.whenReady().then(createWindow); \ No newline at end of file diff --git a/UI/XKey/main.ts b/UI/XKey/main.ts new file mode 100644 index 0000000..c2deae5 --- /dev/null +++ b/UI/XKey/main.ts @@ -0,0 +1,35 @@ +import { BrowserWindow } from "electron"; + +export default class Main { + static mainWindow: BrowserWindow; + static app: Electron.App; + static browserWindow; + + private static onWindowAllClosed(): void { + if (process.platform !== "darwin") Main.app.quit(); + } + + private static onClose(): void { + Main.mainWindow = null; + } + + private static onReady(): void { + Main.mainWindow = new BrowserWindow({ + width: 800, + height: 600, + webPreferences: { + nodeIntegration: true + } + }); + Main.mainWindow.loadFile("main.html"); + Main.mainWindow.on("closed", Main.onClose); + } + + static main(app: Electron.App, browserWindow: typeof BrowserWindow) { + Main.browserWindow = browserWindow; + Main.app = app; + Main.app.on("window-all-closed", Main.onWindowAllClosed); + Main.app.on("ready", Main.onReady); + } + +} \ No newline at end of file diff --git a/UI/XKey/package-lock.json b/UI/XKey/package-lock.json index 808a699..1e26b0b 100644 --- a/UI/XKey/package-lock.json +++ b/UI/XKey/package-lock.json @@ -38,12 +38,43 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.38.tgz", "integrity": "sha512-75eLjX0pFuTcUXnnWmALMzzkYorjND0ezNEycaKesbUBg9eGZp4GHPuDmkRc4mQQvIpe29zrzATNRA6hkYqwmA==" }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, "boolean": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.1.tgz", "integrity": "sha512-HRZPIjPcbwAVQvOTxR4YE3o8Xs98NqbbL1iEZDCz7CL8ql0Lt5iOyJFxfnAB0oFs8Oh02F/lLlg30Mexv46LjA==", "optional": true }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", @@ -83,6 +114,23 @@ } } }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, "clone-response": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", @@ -91,6 +139,27 @@ "mimic-response": "^1.0.0" } }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, "concat-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", @@ -112,6 +181,20 @@ "proto-list": "~1.2.1" } }, + "copyfiles": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.2.0.tgz", + "integrity": "sha512-iJbHJI+8OKqsq+4JF0rqgRkZzo++jqO6Wf4FUU1JM41cJF6JcY5968XyF4tm3Kkm7ZOMrqlljdm8N9oyY5raGw==", + "dev": true, + "requires": { + "glob": "^7.0.5", + "minimatch": "^3.0.3", + "mkdirp": "^0.5.1", + "noms": "0.0.0", + "through2": "^2.0.1", + "yargs": "^13.2.4" + } + }, "core-js": { "version": "3.6.5", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", @@ -131,6 +214,12 @@ "ms": "^2.1.1" } }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, "decompress-response": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", @@ -174,6 +263,12 @@ "extract-zip": "^1.0.3" } }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -239,6 +334,15 @@ "pend": "~1.2.0" } }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, "fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -249,6 +353,18 @@ "universalify": "^0.1.0" } }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -257,6 +373,20 @@ "pump": "^3.0.0" } }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "global-agent": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.1.8.tgz", @@ -321,6 +451,16 @@ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -332,6 +472,12 @@ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "optional": true }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -369,6 +515,16 @@ "resolved": "https://registry.npmjs.org/knockout/-/knockout-3.5.1.tgz", "integrity": "sha512-wRJ9I4az0QcsH7A4v4l0enUpkS++MBx0BnL/68KaLzJg7x1qmbjSlwEoCNol7KTYZ+pmtI7Eh2J0Nu6/2Z5J/Q==" }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", @@ -394,6 +550,15 @@ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", @@ -412,6 +577,42 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "noms": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", + "integrity": "sha1-2o69nzr51nYJGbJ9nNyAkqczKFk=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "~1.0.31" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, "normalize-url": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", @@ -446,6 +647,42 @@ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -501,6 +738,18 @@ "util-deprecate": "~1.0.1" } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, "responselike": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", @@ -557,12 +806,29 @@ "type-fest": "^0.8.0" } }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, "sprintf-js": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", "optional": true }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -571,6 +837,15 @@ "safe-buffer": "~5.1.0" } }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, "sumchecker": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", @@ -579,6 +854,16 @@ "debug": "^4.1.0" } }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, "to-readable-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", @@ -632,11 +917,68 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, "yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", diff --git a/UI/XKey/package.json b/UI/XKey/package.json index 15b934b..6b831b2 100644 --- a/UI/XKey/package.json +++ b/UI/XKey/package.json @@ -5,13 +5,16 @@ "main": "main.js", "type": "module", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "start": "electron ." + "build": "tsc && copyfiles *.html *.css bin/", + "start": "tsc && copyfiles *.html *.css bin/ && electron ./bin/app.js" }, "author": "Logan Komanetz", "license": "MIT", "dependencies": { "electron": "^8.2.5", "knockout": "^3.5.1" + }, + "devDependencies": { + "copyfiles": "^2.2.0" } } diff --git a/UI/XKey/tsconfig.json b/UI/XKey/tsconfig.json new file mode 100644 index 0000000..7dfce4d --- /dev/null +++ b/UI/XKey/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES5", + "module": "CommonJS", + "moduleResolution": "Node", + "sourceMap": true, + "emitDecoratorMetadata": true, + "downlevelIteration": true, + "experimentalDecorators": true, + "removeComments": false, + "noImplicitAny": false, + "suppressImplicitAnyIndexErrors": true, + "outDir": "bin/" + }, + "exclude": [ "node_modules" ], + "include": [ "**/*" ] +} \ No newline at end of file From 4fc99f7995d45ffd67329abfd0515b1655e768af Mon Sep 17 00:00:00 2001 From: lkomanetz Date: Sat, 16 May 2020 10:36:52 -0500 Subject: [PATCH 04/15] Adding keyboard implementation for Windows and refactoring code. --- UI/XKey/app-info.ts | 19 ++++++++++ UI/XKey/converters.ts | 6 +-- UI/XKey/file-system.ts | 19 ++++++---- UI/XKey/key-binding.ts | 16 ++++++-- UI/XKey/keyboard/empty-keyboard.ts | 13 +++++++ UI/XKey/keyboard/index.ts | 3 ++ UI/XKey/keyboard/keyboard-button-interface.ts | 4 +- UI/XKey/keyboard/windows-keyboard.ts | 33 ++++++++-------- UI/XKey/main-view-model.ts | 38 ++++++++++++------- 9 files changed, 104 insertions(+), 47 deletions(-) create mode 100644 UI/XKey/app-info.ts create mode 100644 UI/XKey/keyboard/empty-keyboard.ts create mode 100644 UI/XKey/keyboard/index.ts diff --git a/UI/XKey/app-info.ts b/UI/XKey/app-info.ts new file mode 100644 index 0000000..e9220ee --- /dev/null +++ b/UI/XKey/app-info.ts @@ -0,0 +1,19 @@ +import { IKeyboardButton } from "./keyboard/keyboard-button-interface"; +import { WindowsKeyboard, EmptyKeyboard } from "./keyboard"; + +export class AppInfo { + private _keyboard: IKeyboardButton; + + constructor() { + switch (process.platform) { + case "darwin": + case "linux": + this._keyboard = new EmptyKeyboard(); + break; + default: this._keyboard = new WindowsKeyboard(); + } + } + + get keyboard(): IKeyboardButton { return this._keyboard; } + +} \ No newline at end of file diff --git a/UI/XKey/converters.ts b/UI/XKey/converters.ts index 9afec0b..a20cb69 100644 --- a/UI/XKey/converters.ts +++ b/UI/XKey/converters.ts @@ -2,10 +2,8 @@ import { GamepadButtons } from "./key-binding"; export class GamepadButtonConverter { - convert(buttonId) { + convert(buttonId: number) { return Object.keys(GamepadButtons).find(k => GamepadButtons[k] === buttonId); } -} - -// module.exports = { GamepadButtonConverter }; \ No newline at end of file +} \ No newline at end of file diff --git a/UI/XKey/file-system.ts b/UI/XKey/file-system.ts index 4d5be4a..33464be 100644 --- a/UI/XKey/file-system.ts +++ b/UI/XKey/file-system.ts @@ -1,25 +1,28 @@ import * as fs from "fs"; import { KeyBinding, GamepadButtons } from "./key-binding"; import { GamepadButtonConverter } from "./converters"; +import { IKeyboardButton } from "./keyboard"; export class FileSystem { - private buttonConverter: GamepadButtonConverter; - private encodingStyle: string; + private _buttonConverter: GamepadButtonConverter; + private _encodingStyle: string; + private _keyboard: IKeyboardButton; - constructor(buttonConverter: GamepadButtonConverter, encodingStyle: string) { - this.buttonConverter = buttonConverter; - this.encodingStyle = encodingStyle; + constructor(buttonConverter: GamepadButtonConverter, encodingStyle: string, keyboard: IKeyboardButton) { + this._buttonConverter = buttonConverter; + this._encodingStyle = encodingStyle; + this._keyboard = keyboard; } async loadBindings(location: string): Promise> { let bindings: Array = []; - let data = fs.readFileSync(location, this.encodingStyle).split("\n"); + let data = fs.readFileSync(location, this._encodingStyle).split("\n"); data.forEach(item => { let buttonInfo = item.split("="); - let button = this.buttonConverter.convert(parseInt(buttonInfo[0])); - let mappedKey = buttonInfo[1]; + let button = this._buttonConverter.convert(parseInt(buttonInfo[0])); + let mappedKey = this._keyboard.getKeyName(parseInt(buttonInfo[1], 16)); // I need to remove \n char bindings.push(new KeyBinding(button, mappedKey)); }); diff --git a/UI/XKey/key-binding.ts b/UI/XKey/key-binding.ts index 3289eef..a4841e7 100644 --- a/UI/XKey/key-binding.ts +++ b/UI/XKey/key-binding.ts @@ -1,9 +1,9 @@ export class KeyBinding { - gamepadButton: GamepadButtons; - keyboardButton: any; + gamepadButton: string; + keyboardButton: string; - constructor(gamepadButton, keyboardButton) { + constructor(gamepadButton: string, keyboardButton: string) { this.gamepadButton = gamepadButton; this.keyboardButton = keyboardButton; } @@ -26,4 +26,12 @@ export enum GamepadButtons { DpadUp = 13, DpadDown = 14 } -// module.exports = { KeyBinding, GamepadButtons }; \ No newline at end of file + +export function getGamepadButtonList(): Array { + const buttons = []; + for (const member in GamepadButtons) { + const isValueProperty = parseInt(member, 10) >= 0; + if (isValueProperty) buttons.push(GamepadButtons[member]); + } + return buttons; +} \ No newline at end of file diff --git a/UI/XKey/keyboard/empty-keyboard.ts b/UI/XKey/keyboard/empty-keyboard.ts new file mode 100644 index 0000000..d40ea0a --- /dev/null +++ b/UI/XKey/keyboard/empty-keyboard.ts @@ -0,0 +1,13 @@ +import { IKeyboardButton } from "./keyboard-button-interface"; + +export class EmptyKeyboard implements IKeyboardButton { + + getKeyName(keyCode: number): string { + throw new Error("Method not implemented."); + } + + toKeyCode(keyName: string): number { + throw new Error("Method not implemented."); + } + +} \ No newline at end of file diff --git a/UI/XKey/keyboard/index.ts b/UI/XKey/keyboard/index.ts new file mode 100644 index 0000000..ea9d052 --- /dev/null +++ b/UI/XKey/keyboard/index.ts @@ -0,0 +1,3 @@ +export { EmptyKeyboard } from "./empty-keyboard"; +export { IKeyboardButton } from "./keyboard-button-interface"; +export { WindowsKeyboard } from "./windows-keyboard"; \ No newline at end of file diff --git a/UI/XKey/keyboard/keyboard-button-interface.ts b/UI/XKey/keyboard/keyboard-button-interface.ts index 4329a28..e75f6dc 100644 --- a/UI/XKey/keyboard/keyboard-button-interface.ts +++ b/UI/XKey/keyboard/keyboard-button-interface.ts @@ -1,6 +1,6 @@ export interface IKeyboardButton { - getKeyName(keyCode: string): string; - toKeyCode(keyName: string): string; + getKeyName(keyCode: number): string; + toKeyCode(keyName: string): number; } \ No newline at end of file diff --git a/UI/XKey/keyboard/windows-keyboard.ts b/UI/XKey/keyboard/windows-keyboard.ts index b284eba..d414e42 100644 --- a/UI/XKey/keyboard/windows-keyboard.ts +++ b/UI/XKey/keyboard/windows-keyboard.ts @@ -2,31 +2,32 @@ import { IKeyboardButton } from "./keyboard-button-interface"; export class WindowsKeyboard implements IKeyboardButton { - private _map: Map; + private _map: Map; constructor() { this._map = this.buildMap(); } - getKeyName(keyCode: string): string { - return [...this._map].find(([k, v]) => v === keyCode)[0]; + getKeyName(keyCode: number): string { + return this._map.get(keyCode) ?? "NULL"; } - toKeyCode(keyName: string): string { - return this._map.get(keyName); + toKeyCode(keyName: string): number { + const foundPair = [...this._map].find(([k, v]) => v === keyName); + return (foundPair) ? foundPair[0] : -1; } - private buildMap(): Map { - const keyMap = new Map(); - keyMap.set("Z", "0x5a"); - keyMap.set("X", "0x58"); - keyMap.set("Spacebar", "0x20"); - keyMap.set("Enter", "0x0D"); - keyMap.set("Left Arrow", "0x25"); - keyMap.set("Up Arrow", "0x26"); - keyMap.set("Right Arrow", "0x27"); - keyMap.set("Down Arrow", "0x28"); - return keyMap; + private buildMap(): Map { + return new Map([ + [0x5A, "Z"], + [0x58, "X"], + [0x20, "Spacebar"], + [0x0D, "Enter"], + [0x25, "Left Arrow"], + [0x26, "Up Arrow"], + [0x27, "Right Arrow"], + [0x28, "Down Arrow"] + ]); } } \ No newline at end of file diff --git a/UI/XKey/main-view-model.ts b/UI/XKey/main-view-model.ts index 24d278d..078c34a 100644 --- a/UI/XKey/main-view-model.ts +++ b/UI/XKey/main-view-model.ts @@ -1,7 +1,8 @@ import * as ko from "knockout"; import { remote } from "electron"; import { FileSystem } from "./file-system"; -import { GamepadButtons, KeyBinding} from "./key-binding"; +import { GamepadButtons, KeyBinding, getGamepadButtonList } from "./key-binding"; +import { AppInfo } from "./app-info"; import { GamepadButtonConverter } from "./converters"; const dialog = remote.dialog; @@ -9,12 +10,15 @@ const dialog = remote.dialog; class MainWindowViewModel { private fileSystem: FileSystem; - private bindings: any; private _actionName: any; + private _buttonConverter: GamepadButtonConverter; - constructor(fileSystem: FileSystem) { + bindings: any; + + constructor(fileSystem: FileSystem, buttonConverter: GamepadButtonConverter) { this.fileSystem = fileSystem; this.bindings = ko.observableArray([]); + this._buttonConverter = buttonConverter; this._actionName = ko.observable(""); } @@ -28,22 +32,30 @@ class MainWindowViewModel { this.bindings(bindings); } - private async getFilePath(): Promise { - const result = await dialog.showOpenDialog({ - properties: ["openFile"] - }); - return result.filePaths.length == 1 ? result.filePaths[0] : ""; - } - createBindings() { let bindings = []; - for (const prop in GamepadButtons) { - bindings.push(new KeyBinding(prop, "NULL")); + for (const prop in getGamepadButtonList()) { + const button = this._buttonConverter.convert(parseInt(prop)); + bindings.push(new KeyBinding(button, "NULL")); } this.actionName = "new-bindings-template"; this.bindings(bindings); } + private async getFilePath(): Promise { + const result = await dialog.showOpenDialog({ + properties: ["openFile"] + }); + return result.filePaths.length == 1 ? result.filePaths[0] : ""; + } + } -ko.applyBindings(new MainWindowViewModel(new FileSystem(new GamepadButtonConverter(), "utf8"))); \ No newline at end of file +const appInfo = new AppInfo(); + +ko.applyBindings( + new MainWindowViewModel( + new FileSystem(new GamepadButtonConverter(), "utf8", appInfo.keyboard), + new GamepadButtonConverter() + ) +); \ No newline at end of file From 4a48adf5fab1748d71f655bcb8bd693d87381641 Mon Sep 17 00:00:00 2001 From: lkomanetz Date: Sat, 16 May 2020 14:21:32 -0500 Subject: [PATCH 05/15] Starting work on updating bindings. --- UI/XKey/keyboard/windows-keyboard.ts | 12 ++++----- UI/XKey/main-view-model.ts | 37 ++++++++++++++++++++++++---- UI/XKey/main.html | 2 +- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/UI/XKey/keyboard/windows-keyboard.ts b/UI/XKey/keyboard/windows-keyboard.ts index d414e42..b620e33 100644 --- a/UI/XKey/keyboard/windows-keyboard.ts +++ b/UI/XKey/keyboard/windows-keyboard.ts @@ -21,12 +21,12 @@ export class WindowsKeyboard implements IKeyboardButton { return new Map([ [0x5A, "Z"], [0x58, "X"], - [0x20, "Spacebar"], - [0x0D, "Enter"], - [0x25, "Left Arrow"], - [0x26, "Up Arrow"], - [0x27, "Right Arrow"], - [0x28, "Down Arrow"] + [0x20, "SPACEBAR"], + [0x0D, "ENTER"], + [0x25, "ARROWLEFT"], + [0x26, "ARROWUP"], + [0x27, "ARROWRIGHT"], + [0x28, "ARROWDOWN"] ]); } diff --git a/UI/XKey/main-view-model.ts b/UI/XKey/main-view-model.ts index 078c34a..2d08e3d 100644 --- a/UI/XKey/main-view-model.ts +++ b/UI/XKey/main-view-model.ts @@ -4,6 +4,7 @@ import { FileSystem } from "./file-system"; import { GamepadButtons, KeyBinding, getGamepadButtonList } from "./key-binding"; import { AppInfo } from "./app-info"; import { GamepadButtonConverter } from "./converters"; +import { IKeyboardButton } from "./keyboard"; const dialog = remote.dialog; @@ -12,10 +13,16 @@ class MainWindowViewModel { private fileSystem: FileSystem; private _actionName: any; private _buttonConverter: GamepadButtonConverter; + private _keyboard: IKeyboardButton; bindings: any; - constructor(fileSystem: FileSystem, buttonConverter: GamepadButtonConverter) { + constructor( + fileSystem: FileSystem, + buttonConverter: GamepadButtonConverter, + keyboard: IKeyboardButton + ) { + this._keyboard = keyboard; this.fileSystem = fileSystem; this.bindings = ko.observableArray([]); this._buttonConverter = buttonConverter; @@ -29,17 +36,17 @@ class MainWindowViewModel { const filePath = await this.getFilePath(); const bindings = await this.fileSystem.loadBindings(filePath); this.actionName = "update-bindings-template"; - this.bindings(bindings); + this.bindings(bindings.map(kb => new KeyBindingViewModel(kb, this._keyboard))); } createBindings() { - let bindings = []; + let bindings: Array = []; for (const prop in getGamepadButtonList()) { const button = this._buttonConverter.convert(parseInt(prop)); bindings.push(new KeyBinding(button, "NULL")); } this.actionName = "new-bindings-template"; - this.bindings(bindings); + this.bindings(bindings.map(kb => new KeyBindingViewModel(kb, this._keyboard))); } private async getFilePath(): Promise { @@ -51,11 +58,31 @@ class MainWindowViewModel { } +class KeyBindingViewModel { + + private readonly _keyboard: IKeyboardButton; + + public keyboardButton: any; + public gamepadButton: any; + + constructor(binding: KeyBinding, keyboard: IKeyboardButton) { + this.keyboardButton = ko.observable(binding.keyboardButton); + this.gamepadButton = ko.observable(binding.gamepadButton); + this._keyboard = keyboard; + } + + updateBinding(binding:KeyBindingViewModel, evt: KeyboardEvent): void { + const key: string = this._keyboard.getKeyName(evt.keyCode); + this.keyboardButton(key); + } +} + const appInfo = new AppInfo(); ko.applyBindings( new MainWindowViewModel( new FileSystem(new GamepadButtonConverter(), "utf8", appInfo.keyboard), - new GamepadButtonConverter() + new GamepadButtonConverter(), + appInfo.keyboard ) ); \ No newline at end of file diff --git a/UI/XKey/main.html b/UI/XKey/main.html index 1b0a109..dff751b 100644 --- a/UI/XKey/main.html +++ b/UI/XKey/main.html @@ -24,7 +24,7 @@ - + From c97e9bad1d7ebe0654aa22382ba6c4b42213007d Mon Sep 17 00:00:00 2001 From: lkomanetz Date: Mon, 18 May 2020 09:44:44 -0500 Subject: [PATCH 06/15] Fixing issues with detecting which key was pressed and preventing default behavior. --- UI/XKey/file-system.ts | 8 ++--- UI/XKey/key-binding.ts | 4 +-- UI/XKey/keyboard/windows-keyboard.ts | 1 + UI/XKey/main-view-model.ts | 45 +++++++++++++++------------- UI/XKey/main.html | 2 +- 5 files changed, 31 insertions(+), 29 deletions(-) diff --git a/UI/XKey/file-system.ts b/UI/XKey/file-system.ts index 33464be..f7d4b8f 100644 --- a/UI/XKey/file-system.ts +++ b/UI/XKey/file-system.ts @@ -1,18 +1,15 @@ import * as fs from "fs"; import { KeyBinding, GamepadButtons } from "./key-binding"; import { GamepadButtonConverter } from "./converters"; -import { IKeyboardButton } from "./keyboard"; export class FileSystem { private _buttonConverter: GamepadButtonConverter; private _encodingStyle: string; - private _keyboard: IKeyboardButton; - constructor(buttonConverter: GamepadButtonConverter, encodingStyle: string, keyboard: IKeyboardButton) { + constructor(buttonConverter: GamepadButtonConverter, encodingStyle: string) { this._buttonConverter = buttonConverter; this._encodingStyle = encodingStyle; - this._keyboard = keyboard; } async loadBindings(location: string): Promise> { @@ -22,8 +19,7 @@ export class FileSystem { data.forEach(item => { let buttonInfo = item.split("="); let button = this._buttonConverter.convert(parseInt(buttonInfo[0])); - let mappedKey = this._keyboard.getKeyName(parseInt(buttonInfo[1], 16)); // I need to remove \n char - bindings.push(new KeyBinding(button, mappedKey)); + bindings.push(new KeyBinding(button, parseInt(buttonInfo[1], 16))); }); return bindings; diff --git a/UI/XKey/key-binding.ts b/UI/XKey/key-binding.ts index a4841e7..1a7c84d 100644 --- a/UI/XKey/key-binding.ts +++ b/UI/XKey/key-binding.ts @@ -1,9 +1,9 @@ export class KeyBinding { gamepadButton: string; - keyboardButton: string; + keyboardButton: number; - constructor(gamepadButton: string, keyboardButton: string) { + constructor(gamepadButton: string, keyboardButton: number) { this.gamepadButton = gamepadButton; this.keyboardButton = keyboardButton; } diff --git a/UI/XKey/keyboard/windows-keyboard.ts b/UI/XKey/keyboard/windows-keyboard.ts index b620e33..4bfb22e 100644 --- a/UI/XKey/keyboard/windows-keyboard.ts +++ b/UI/XKey/keyboard/windows-keyboard.ts @@ -9,6 +9,7 @@ export class WindowsKeyboard implements IKeyboardButton { } getKeyName(keyCode: number): string { + return this._map.get(keyCode) ?? "NULL"; } diff --git a/UI/XKey/main-view-model.ts b/UI/XKey/main-view-model.ts index 2d08e3d..f6210e3 100644 --- a/UI/XKey/main-view-model.ts +++ b/UI/XKey/main-view-model.ts @@ -1,10 +1,9 @@ import * as ko from "knockout"; import { remote } from "electron"; import { FileSystem } from "./file-system"; -import { GamepadButtons, KeyBinding, getGamepadButtonList } from "./key-binding"; +import { KeyBinding, getGamepadButtonList } from "./key-binding"; import { AppInfo } from "./app-info"; import { GamepadButtonConverter } from "./converters"; -import { IKeyboardButton } from "./keyboard"; const dialog = remote.dialog; @@ -13,16 +12,13 @@ class MainWindowViewModel { private fileSystem: FileSystem; private _actionName: any; private _buttonConverter: GamepadButtonConverter; - private _keyboard: IKeyboardButton; bindings: any; constructor( fileSystem: FileSystem, - buttonConverter: GamepadButtonConverter, - keyboard: IKeyboardButton + buttonConverter: GamepadButtonConverter ) { - this._keyboard = keyboard; this.fileSystem = fileSystem; this.bindings = ko.observableArray([]); this._buttonConverter = buttonConverter; @@ -36,17 +32,17 @@ class MainWindowViewModel { const filePath = await this.getFilePath(); const bindings = await this.fileSystem.loadBindings(filePath); this.actionName = "update-bindings-template"; - this.bindings(bindings.map(kb => new KeyBindingViewModel(kb, this._keyboard))); + this.bindings(bindings.map(kb => new KeyBindingViewModel(kb))); } createBindings() { let bindings: Array = []; for (const prop in getGamepadButtonList()) { const button = this._buttonConverter.convert(parseInt(prop)); - bindings.push(new KeyBinding(button, "NULL")); + bindings.push(new KeyBinding(button, -1)); } this.actionName = "new-bindings-template"; - this.bindings(bindings.map(kb => new KeyBindingViewModel(kb, this._keyboard))); + this.bindings(bindings.map(kb => new KeyBindingViewModel(kb))); } private async getFilePath(): Promise { @@ -60,29 +56,38 @@ class MainWindowViewModel { class KeyBindingViewModel { - private readonly _keyboard: IKeyboardButton; - - public keyboardButton: any; + public keyboardCode: any; public gamepadButton: any; + public keyboardButton: any; - constructor(binding: KeyBinding, keyboard: IKeyboardButton) { - this.keyboardButton = ko.observable(binding.keyboardButton); + constructor(binding: KeyBinding) { + this.keyboardCode = ko.observable(binding.keyboardButton); this.gamepadButton = ko.observable(binding.gamepadButton); - this._keyboard = keyboard; + this.keyboardButton = ko.observable("NULL"); } updateBinding(binding:KeyBindingViewModel, evt: KeyboardEvent): void { - const key: string = this._keyboard.getKeyName(evt.keyCode); - this.keyboardButton(key); + this.keyboardCode(evt.keyCode); + this.keyboardButton(this.getButton(evt)); + evt.preventDefault(); + } + + toContract(): KeyBinding { + return new KeyBinding(this.gamepadButton(), this.keyboardCode()); + } + + private getButton(evt: KeyboardEvent) { + if (evt.keyCode === 0x20) return "Spacebar"; + return evt.key; } + } const appInfo = new AppInfo(); ko.applyBindings( new MainWindowViewModel( - new FileSystem(new GamepadButtonConverter(), "utf8", appInfo.keyboard), - new GamepadButtonConverter(), - appInfo.keyboard + new FileSystem(new GamepadButtonConverter(), "utf8"), + new GamepadButtonConverter() ) ); \ No newline at end of file diff --git a/UI/XKey/main.html b/UI/XKey/main.html index dff751b..4d5876a 100644 --- a/UI/XKey/main.html +++ b/UI/XKey/main.html @@ -24,7 +24,7 @@ - + From d9f6af384fd47c43906da47beff5b9adbc9de608 Mon Sep 17 00:00:00 2001 From: lkomanetz Date: Mon, 18 May 2020 10:57:15 -0500 Subject: [PATCH 07/15] Continued work with updating key bindings. --- UI/XKey/file-system.ts | 13 +++++++++++-- UI/XKey/keyboard/windows-keyboard.ts | 14 +++++++++++++- UI/XKey/main-view-model.ts | 6 +++++- UI/XKey/main.html | 4 ++-- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/UI/XKey/file-system.ts b/UI/XKey/file-system.ts index f7d4b8f..9c84431 100644 --- a/UI/XKey/file-system.ts +++ b/UI/XKey/file-system.ts @@ -12,7 +12,7 @@ export class FileSystem { this._encodingStyle = encodingStyle; } - async loadBindings(location: string): Promise> { + loadBindings(location: string): Promise> { let bindings: Array = []; let data = fs.readFileSync(location, this._encodingStyle).split("\n"); @@ -22,7 +22,16 @@ export class FileSystem { bindings.push(new KeyBinding(button, parseInt(buttonInfo[1], 16))); }); - return bindings; + return Promise.resolve(bindings); + } + + saveBindings(location: string, bindings: Array): Promise { + const fileContents = bindings + .map(kb => `${kb.gamepadButton}=${kb.keyboardButton}`) + .join("\n"); + + fs.writeFile(location, fileContents, { encoding: "utf8" }, e => {}); + return Promise.resolve(); } } \ No newline at end of file diff --git a/UI/XKey/keyboard/windows-keyboard.ts b/UI/XKey/keyboard/windows-keyboard.ts index 4bfb22e..5c27b0c 100644 --- a/UI/XKey/keyboard/windows-keyboard.ts +++ b/UI/XKey/keyboard/windows-keyboard.ts @@ -3,13 +3,14 @@ import { IKeyboardButton } from "./keyboard-button-interface"; export class WindowsKeyboard implements IKeyboardButton { private _map: Map; + private _specialChars: Map; constructor() { this._map = this.buildMap(); + this._specialChars = this.specialCharacterSet(); } getKeyName(keyCode: number): string { - return this._map.get(keyCode) ?? "NULL"; } @@ -31,4 +32,15 @@ export class WindowsKeyboard implements IKeyboardButton { ]); } + private specialCharacterSet(): Map { + return new Map([ + [0x20, "SPACEBAR"], + [0x0D, "ENTER"], + [0x25, "ARROWLEFT"], + [0x26, "ARROWUP"], + [0x27, "ARROWRIGHT"], + [0x28, "ARROWDOWN"] + ]); + } + } \ No newline at end of file diff --git a/UI/XKey/main-view-model.ts b/UI/XKey/main-view-model.ts index f6210e3..da0da91 100644 --- a/UI/XKey/main-view-model.ts +++ b/UI/XKey/main-view-model.ts @@ -45,6 +45,10 @@ class MainWindowViewModel { this.bindings(bindings.map(kb => new KeyBindingViewModel(kb))); } + saveBindings() { + console.log(this.bindings()); + } + private async getFilePath(): Promise { const result = await dialog.showOpenDialog({ properties: ["openFile"] @@ -63,7 +67,7 @@ class KeyBindingViewModel { constructor(binding: KeyBinding) { this.keyboardCode = ko.observable(binding.keyboardButton); this.gamepadButton = ko.observable(binding.gamepadButton); - this.keyboardButton = ko.observable("NULL"); + this.keyboardButton = ko.observable(String.fromCodePoint(binding.keyboardButton)); } updateBinding(binding:KeyBindingViewModel, evt: KeyboardEvent): void { diff --git a/UI/XKey/main.html b/UI/XKey/main.html index 4d5876a..502fae6 100644 --- a/UI/XKey/main.html +++ b/UI/XKey/main.html @@ -34,10 +34,10 @@ \ No newline at end of file From cbaa8bc0a29b93bfc19133f141d67f176eddec2a Mon Sep 17 00:00:00 2001 From: lkomanetz Date: Fri, 5 Jun 2020 12:36:01 -0500 Subject: [PATCH 08/15] Working on the keyboard interaction. --- UI/XKey/converters.ts | 13 +++++++++++ UI/XKey/keyboard/windows-keyboard.ts | 20 +++++++++++++++-- UI/XKey/main-view-model.ts | 33 ++++++++++++++++++---------- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/UI/XKey/converters.ts b/UI/XKey/converters.ts index a20cb69..38b1a42 100644 --- a/UI/XKey/converters.ts +++ b/UI/XKey/converters.ts @@ -6,4 +6,17 @@ export class GamepadButtonConverter { return Object.keys(GamepadButtons).find(k => GamepadButtons[k] === buttonId); } +} + +export class KeyboardCodeConverter { + + convert(keyCode: number): string { + try { + return String.fromCharCode(keyCode); + } + catch (err) { + return "NULL"; + } + } + } \ No newline at end of file diff --git a/UI/XKey/keyboard/windows-keyboard.ts b/UI/XKey/keyboard/windows-keyboard.ts index 5c27b0c..ad6fe81 100644 --- a/UI/XKey/keyboard/windows-keyboard.ts +++ b/UI/XKey/keyboard/windows-keyboard.ts @@ -11,7 +11,7 @@ export class WindowsKeyboard implements IKeyboardButton { } getKeyName(keyCode: number): string { - return this._map.get(keyCode) ?? "NULL"; + return this._specialChars.get(keyCode) ?? String.fromCharCode(keyCode).toUpperCase(); } toKeyCode(keyName: string): number { @@ -34,12 +34,28 @@ export class WindowsKeyboard implements IKeyboardButton { private specialCharacterSet(): Map { return new Map([ + [NaN, "NULL"], [0x20, "SPACEBAR"], [0x0D, "ENTER"], [0x25, "ARROWLEFT"], [0x26, "ARROWUP"], [0x27, "ARROWRIGHT"], - [0x28, "ARROWDOWN"] + [0x28, "ARROWDOWN"], + [0x09, "TAB"], + [0x60, "NUMPAD_0"], + [0x61, "NUMPAD_1"], + [0x62, "NUMPAD_2"], + [0x63, "NUMPAD_3"], + [0x64, "NUMPAD_4"], + [0x65, "NUMPAD_5"], + [0x66, "NUMPAD_6"], + [0x67, "NUMPAD_7"], + [0x68, "NUMPAD_8"], + [0x69, "NUMPAD_9"], + [0xA0, "Left Shift"], + [0xA1, "Right Shift"], + [0xA2, "Left Control"], + [0xA3, "Right Control"] ]); } diff --git a/UI/XKey/main-view-model.ts b/UI/XKey/main-view-model.ts index da0da91..143a6d2 100644 --- a/UI/XKey/main-view-model.ts +++ b/UI/XKey/main-view-model.ts @@ -1,9 +1,10 @@ import * as ko from "knockout"; import { remote } from "electron"; import { FileSystem } from "./file-system"; +import { IKeyboardButton, WindowsKeyboard } from "./keyboard"; import { KeyBinding, getGamepadButtonList } from "./key-binding"; import { AppInfo } from "./app-info"; -import { GamepadButtonConverter } from "./converters"; +import { GamepadButtonConverter, KeyboardCodeConverter } from "./converters"; const dialog = remote.dialog; @@ -12,15 +13,18 @@ class MainWindowViewModel { private fileSystem: FileSystem; private _actionName: any; private _buttonConverter: GamepadButtonConverter; + private _keyboard: IKeyboardButton; bindings: any; constructor( fileSystem: FileSystem, - buttonConverter: GamepadButtonConverter + buttonConverter: GamepadButtonConverter, + keyboard: IKeyboardButton ) { this.fileSystem = fileSystem; this.bindings = ko.observableArray([]); + this._keyboard = keyboard; this._buttonConverter = buttonConverter; this._actionName = ko.observable(""); } @@ -32,7 +36,7 @@ class MainWindowViewModel { const filePath = await this.getFilePath(); const bindings = await this.fileSystem.loadBindings(filePath); this.actionName = "update-bindings-template"; - this.bindings(bindings.map(kb => new KeyBindingViewModel(kb))); + this.bindings(bindings.map(kb => new KeyBindingViewModel(kb, this._keyboard))); } createBindings() { @@ -42,7 +46,7 @@ class MainWindowViewModel { bindings.push(new KeyBinding(button, -1)); } this.actionName = "new-bindings-template"; - this.bindings(bindings.map(kb => new KeyBindingViewModel(kb))); + this.bindings(bindings.map(kb => new KeyBindingViewModel(kb, this._keyboard))); } saveBindings() { @@ -63,11 +67,14 @@ class KeyBindingViewModel { public keyboardCode: any; public gamepadButton: any; public keyboardButton: any; - - constructor(binding: KeyBinding) { - this.keyboardCode = ko.observable(binding.keyboardButton); - this.gamepadButton = ko.observable(binding.gamepadButton); - this.keyboardButton = ko.observable(String.fromCodePoint(binding.keyboardButton)); + + private _keyboard: IKeyboardButton; + + constructor(binding: KeyBinding, keyboard: IKeyboardButton) { + this._keyboard = keyboard; + this.keyboardCode = ko.observable(binding.keyboardButton); + this.gamepadButton = ko.observable(binding.gamepadButton); + this.keyboardButton = ko.observable(keyboard.getKeyName(binding.keyboardButton)); } updateBinding(binding:KeyBindingViewModel, evt: KeyboardEvent): void { @@ -81,8 +88,9 @@ class KeyBindingViewModel { } private getButton(evt: KeyboardEvent) { - if (evt.keyCode === 0x20) return "Spacebar"; - return evt.key; + // if (evt.keyCode === 0x20) return "Spacebar"; + // return evt.key; + return this._keyboard.getKeyName(evt.keyCode); } } @@ -92,6 +100,7 @@ const appInfo = new AppInfo(); ko.applyBindings( new MainWindowViewModel( new FileSystem(new GamepadButtonConverter(), "utf8"), - new GamepadButtonConverter() + new GamepadButtonConverter(), + appInfo.keyboard ) ); \ No newline at end of file From f4ff3d642d08fe18630f7250cc862dff1a3ec44c Mon Sep 17 00:00:00 2001 From: lkomanetz Date: Sat, 6 Jun 2020 22:22:22 -0500 Subject: [PATCH 09/15] reading windows key codes from MSDN website. --- UI/XKey/keyboard/windows-keyboard.ts | 86 ++++++++++++++-------------- UI/XKey/main-view-model.ts | 5 +- UI/XKey/package-lock.json | 29 ++++++++++ UI/XKey/package.json | 1 + 4 files changed, 76 insertions(+), 45 deletions(-) diff --git a/UI/XKey/keyboard/windows-keyboard.ts b/UI/XKey/keyboard/windows-keyboard.ts index ad6fe81..3b58682 100644 --- a/UI/XKey/keyboard/windows-keyboard.ts +++ b/UI/XKey/keyboard/windows-keyboard.ts @@ -1,62 +1,64 @@ import { IKeyboardButton } from "./keyboard-button-interface"; +import { remote, ClientRequest } from "electron"; export class WindowsKeyboard implements IKeyboardButton { - private _map: Map; - private _specialChars: Map; + private _characterMap: Map; constructor() { - this._map = this.buildMap(); - this._specialChars = this.specialCharacterSet(); + this._characterMap = new Map(); + this.buildCharacterSetAsync() + .then(chars => this._characterMap = chars); } getKeyName(keyCode: number): string { - return this._specialChars.get(keyCode) ?? String.fromCharCode(keyCode).toUpperCase(); + return this._characterMap.get(keyCode) ?? String.fromCharCode(keyCode).toUpperCase(); } toKeyCode(keyName: string): number { - const foundPair = [...this._map].find(([k, v]) => v === keyName); + const alphaNumericResult = keyName.match(/[A-Za-z0-9]/); + if (alphaNumericResult) return keyName.charCodeAt(0); + const foundPair = [...this._characterMap].find(([k, v]) => v === keyName); return (foundPair) ? foundPair[0] : -1; } - private buildMap(): Map { - return new Map([ - [0x5A, "Z"], - [0x58, "X"], - [0x20, "SPACEBAR"], - [0x0D, "ENTER"], - [0x25, "ARROWLEFT"], - [0x26, "ARROWUP"], - [0x27, "ARROWRIGHT"], - [0x28, "ARROWDOWN"] - ]); + private isInvalidKeyCode(elementCollection: HTMLCollectionOf): boolean { + return ( + !elementCollection || + elementCollection[0].innerText === "" || + elementCollection[0].innerText.length === 1 || + elementCollection[0].innerText.includes("Minimum") || + elementCollection[0].innerText.includes("Header") || + elementCollection[0].innerText.includes("Winuser") + ); } - private specialCharacterSet(): Map { - return new Map([ - [NaN, "NULL"], - [0x20, "SPACEBAR"], - [0x0D, "ENTER"], - [0x25, "ARROWLEFT"], - [0x26, "ARROWUP"], - [0x27, "ARROWRIGHT"], - [0x28, "ARROWDOWN"], - [0x09, "TAB"], - [0x60, "NUMPAD_0"], - [0x61, "NUMPAD_1"], - [0x62, "NUMPAD_2"], - [0x63, "NUMPAD_3"], - [0x64, "NUMPAD_4"], - [0x65, "NUMPAD_5"], - [0x66, "NUMPAD_6"], - [0x67, "NUMPAD_7"], - [0x68, "NUMPAD_8"], - [0x69, "NUMPAD_9"], - [0xA0, "Left Shift"], - [0xA1, "Right Shift"], - [0xA2, "Left Control"], - [0xA3, "Right Control"] - ]); + private buildCharacterSetAsync(): Promise> { + return new Promise((resolve, reject) => { + let specialChars = new Map(); + specialChars.set(NaN, "NULL"); + + const textDecoder = new TextDecoder("utf-8"); + const domParser = new DOMParser(); + const url = "https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes"; + const request = remote.net.request(url); + request.on("response", r => { + r.on("end", () => resolve(specialChars)); + r.on("data", chunk => { + const html = domParser.parseFromString(textDecoder.decode(chunk), "text/html"); + const keyCodes = html.getElementsByTagName("dl"); + for (let i = 0; i < keyCodes.length; ++i) { + const dt = keyCodes[i].getElementsByTagName("dt"); + if (this.isInvalidKeyCode(dt)) continue; + const keyText = dt[0].innerText; + const keyCode = parseInt(dt[1].innerText, 16); + console.log(`KeyCode: ${keyCode} , KeyText: ${keyText}`); + specialChars.set(keyCode, keyText); + } + }); + }); + request.end(); + }); } } \ No newline at end of file diff --git a/UI/XKey/main-view-model.ts b/UI/XKey/main-view-model.ts index 143a6d2..10e3f14 100644 --- a/UI/XKey/main-view-model.ts +++ b/UI/XKey/main-view-model.ts @@ -78,7 +78,8 @@ class KeyBindingViewModel { } updateBinding(binding:KeyBindingViewModel, evt: KeyboardEvent): void { - this.keyboardCode(evt.keyCode); + const keyCode = this._keyboard.toKeyCode(evt.key.toUpperCase()); + this.keyboardCode(keyCode); this.keyboardButton(this.getButton(evt)); evt.preventDefault(); } @@ -88,8 +89,6 @@ class KeyBindingViewModel { } private getButton(evt: KeyboardEvent) { - // if (evt.keyCode === 0x20) return "Spacebar"; - // return evt.key; return this._keyboard.getKeyName(evt.keyCode); } diff --git a/UI/XKey/package-lock.json b/UI/XKey/package-lock.json index 1e26b0b..bb4d8e8 100644 --- a/UI/XKey/package-lock.json +++ b/UI/XKey/package-lock.json @@ -263,6 +263,14 @@ "extract-zip": "^1.0.3" } }, + "electron-fetch": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/electron-fetch/-/electron-fetch-1.4.0.tgz", + "integrity": "sha512-rednYIpMbuzekTroNndQOFl95c4I/wMEbH9jxGoDEoKrM07b7FWydy6I3pbiAbCxDcYpmHtzMY6ykyLagR7JHw==", + "requires": { + "encoding": "^0.1.12" + } + }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", @@ -275,6 +283,14 @@ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", "optional": true }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "~0.4.13" + } + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -451,6 +467,14 @@ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -777,6 +801,11 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "sanitize-filename": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", diff --git a/UI/XKey/package.json b/UI/XKey/package.json index 6b831b2..4268b1d 100644 --- a/UI/XKey/package.json +++ b/UI/XKey/package.json @@ -12,6 +12,7 @@ "license": "MIT", "dependencies": { "electron": "^8.2.5", + "electron-fetch": "^1.4.0", "knockout": "^3.5.1" }, "devDependencies": { From f16037de9b916a2730f434c02653b46653788d51 Mon Sep 17 00:00:00 2001 From: lkomanetz Date: Sun, 7 Jun 2020 09:53:25 -0500 Subject: [PATCH 10/15] Fixing bugs with updating keybindings. --- UI/XKey/file-system.ts | 8 +++++--- UI/XKey/keyboard/windows-keyboard.ts | 12 +++++++----- UI/XKey/main-view-model.ts | 8 ++++++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/UI/XKey/file-system.ts b/UI/XKey/file-system.ts index 9c84431..0a48e8e 100644 --- a/UI/XKey/file-system.ts +++ b/UI/XKey/file-system.ts @@ -19,7 +19,8 @@ export class FileSystem { data.forEach(item => { let buttonInfo = item.split("="); let button = this._buttonConverter.convert(parseInt(buttonInfo[0])); - bindings.push(new KeyBinding(button, parseInt(buttonInfo[1], 16))); + const keyCode = (buttonInfo[1].trim() === "NULL") ? -1 : parseInt(buttonInfo[1], 16); + bindings.push(new KeyBinding(button, keyCode)); }); return Promise.resolve(bindings); @@ -27,10 +28,11 @@ export class FileSystem { saveBindings(location: string, bindings: Array): Promise { const fileContents = bindings - .map(kb => `${kb.gamepadButton}=${kb.keyboardButton}`) + .map(kb => `${kb.gamepadButton}=${kb.keyboardButton === -1 ? 'NULL' : kb.keyboardButton}`) .join("\n"); - fs.writeFile(location, fileContents, { encoding: "utf8" }, e => {}); + // fs.writeFile(location, fileContents, { encoding: "utf8" }, e => {}); + console.log(fileContents); return Promise.resolve(); } diff --git a/UI/XKey/keyboard/windows-keyboard.ts b/UI/XKey/keyboard/windows-keyboard.ts index 3b58682..f3f5e38 100644 --- a/UI/XKey/keyboard/windows-keyboard.ts +++ b/UI/XKey/keyboard/windows-keyboard.ts @@ -15,8 +15,9 @@ export class WindowsKeyboard implements IKeyboardButton { return this._characterMap.get(keyCode) ?? String.fromCharCode(keyCode).toUpperCase(); } + // TODO(Logan) -> Figure out why special keys end up as NULL toKeyCode(keyName: string): number { - const alphaNumericResult = keyName.match(/[A-Za-z0-9]/); + const alphaNumericResult = keyName.match(/^[A-Za-z0-9]{1}$/); if (alphaNumericResult) return keyName.charCodeAt(0); const foundPair = [...this._characterMap].find(([k, v]) => v === keyName); return (foundPair) ? foundPair[0] : -1; @@ -36,7 +37,6 @@ export class WindowsKeyboard implements IKeyboardButton { private buildCharacterSetAsync(): Promise> { return new Promise((resolve, reject) => { let specialChars = new Map(); - specialChars.set(NaN, "NULL"); const textDecoder = new TextDecoder("utf-8"); const domParser = new DOMParser(); @@ -50,10 +50,12 @@ export class WindowsKeyboard implements IKeyboardButton { for (let i = 0; i < keyCodes.length; ++i) { const dt = keyCodes[i].getElementsByTagName("dt"); if (this.isInvalidKeyCode(dt)) continue; - const keyText = dt[0].innerText; + const isVirtualKey = dt[0].innerText.includes("VK_"); + const keyText = (isVirtualKey) ? dt[0].innerText.substring(dt[0].innerText.indexOf("_") + 1) : dt[0].innerText; const keyCode = parseInt(dt[1].innerText, 16); - console.log(`KeyCode: ${keyCode} , KeyText: ${keyText}`); - specialChars.set(keyCode, keyText); + + if (keyText === "ESCAPE") specialChars.set(-1, "NULL"); + else specialChars.set(keyCode, keyText); } }); }); diff --git a/UI/XKey/main-view-model.ts b/UI/XKey/main-view-model.ts index 10e3f14..6c9ae0f 100644 --- a/UI/XKey/main-view-model.ts +++ b/UI/XKey/main-view-model.ts @@ -14,6 +14,7 @@ class MainWindowViewModel { private _actionName: any; private _buttonConverter: GamepadButtonConverter; private _keyboard: IKeyboardButton; + private _currentFilePath: string; bindings: any; @@ -37,6 +38,7 @@ class MainWindowViewModel { const bindings = await this.fileSystem.loadBindings(filePath); this.actionName = "update-bindings-template"; this.bindings(bindings.map(kb => new KeyBindingViewModel(kb, this._keyboard))); + this._currentFilePath = filePath; } createBindings() { @@ -50,7 +52,8 @@ class MainWindowViewModel { } saveBindings() { - console.log(this.bindings()); + if (!this._currentFilePath) this._currentFilePath = dialog.showSaveDialogSync({}); + this.fileSystem.saveBindings(this._currentFilePath, this.bindings().map(b => b.toContract())); } private async getFilePath(): Promise { @@ -89,7 +92,8 @@ class KeyBindingViewModel { } private getButton(evt: KeyboardEvent) { - return this._keyboard.getKeyName(evt.keyCode); + if (evt.key === "Escape") return this._keyboard.getKeyName(-1); + else return this._keyboard.getKeyName(evt.keyCode); } } From 6db54c0ed8e45029afa14698a692669753b52748 Mon Sep 17 00:00:00 2001 From: lkomanetz Date: Wed, 15 Jul 2020 12:59:18 -0500 Subject: [PATCH 11/15] Fixing bugs with saving/updating key bindings. --- UI/XKey/converters.ts | 7 +++++- UI/XKey/file-system.ts | 47 ++++++++++++++++++++++++-------------- UI/XKey/main-view-model.ts | 17 ++++++++++++-- 3 files changed, 51 insertions(+), 20 deletions(-) diff --git a/UI/XKey/converters.ts b/UI/XKey/converters.ts index 38b1a42..26dd992 100644 --- a/UI/XKey/converters.ts +++ b/UI/XKey/converters.ts @@ -2,10 +2,15 @@ import { GamepadButtons } from "./key-binding"; export class GamepadButtonConverter { - convert(buttonId: number) { + toButtonName(buttonId: number) { return Object.keys(GamepadButtons).find(k => GamepadButtons[k] === buttonId); } + toButtonId(buttonName: string): number { + const enumVal = Object.values(GamepadButtons).find(name => name === buttonName); + return GamepadButtons[enumVal]; + } + } export class KeyboardCodeConverter { diff --git a/UI/XKey/file-system.ts b/UI/XKey/file-system.ts index 0a48e8e..6967d94 100644 --- a/UI/XKey/file-system.ts +++ b/UI/XKey/file-system.ts @@ -13,27 +13,40 @@ export class FileSystem { } loadBindings(location: string): Promise> { - let bindings: Array = []; - let data = fs.readFileSync(location, this._encodingStyle).split("\n"); - - data.forEach(item => { - let buttonInfo = item.split("="); - let button = this._buttonConverter.convert(parseInt(buttonInfo[0])); - const keyCode = (buttonInfo[1].trim() === "NULL") ? -1 : parseInt(buttonInfo[1], 16); - bindings.push(new KeyBinding(button, keyCode)); + return new Promise>((resolve, reject) => { + let bindings: Array = []; + let data = fs.readFileSync(location, this._encodingStyle).split("\n"); + + data.forEach(item => { + let buttonInfo = item.split("="); + let button = this._buttonConverter.toButtonName(parseInt(buttonInfo[0])); + const keyCode = (buttonInfo[1].trim() === "NULL") ? -1 : parseInt(buttonInfo[1], 16); + bindings.push(new KeyBinding(button, keyCode)); + }); + resolve(bindings); }); - - return Promise.resolve(bindings); } saveBindings(location: string, bindings: Array): Promise { - const fileContents = bindings - .map(kb => `${kb.gamepadButton}=${kb.keyboardButton === -1 ? 'NULL' : kb.keyboardButton}`) - .join("\n"); - - // fs.writeFile(location, fileContents, { encoding: "utf8" }, e => {}); - console.log(fileContents); - return Promise.resolve(); + return new Promise((resolve, reject) => { + try { + const fileContents = bindings + .map(b => this.toOutputString(b)) + .join("\n"); + + fs.writeFileSync(location, fileContents, { encoding: "utf8"}); + resolve(); + } + catch (err) { + reject(err); + } + }); + } + + private toOutputString(binding: KeyBinding): string { + const toHex = (n: number) => n.toString(16).toUpperCase(); + const buttonId = this._buttonConverter.toButtonId(binding.gamepadButton); + return `${buttonId}=${binding.keyboardButton === -1 ? 'NULL' : toHex(binding.keyboardButton)}`; } } \ No newline at end of file diff --git a/UI/XKey/main-view-model.ts b/UI/XKey/main-view-model.ts index 6c9ae0f..b301581 100644 --- a/UI/XKey/main-view-model.ts +++ b/UI/XKey/main-view-model.ts @@ -44,7 +44,7 @@ class MainWindowViewModel { createBindings() { let bindings: Array = []; for (const prop in getGamepadButtonList()) { - const button = this._buttonConverter.convert(parseInt(prop)); + const button = this._buttonConverter.toButtonName(parseInt(prop)); bindings.push(new KeyBinding(button, -1)); } this.actionName = "new-bindings-template"; @@ -53,6 +53,9 @@ class MainWindowViewModel { saveBindings() { if (!this._currentFilePath) this._currentFilePath = dialog.showSaveDialogSync({}); + this._currentFilePath = (!this.containsFileExtension(this._currentFilePath)) ? + this.appendFileExtensionTo(this._currentFilePath) : + this._currentFilePath; this.fileSystem.saveBindings(this._currentFilePath, this.bindings().map(b => b.toContract())); } @@ -63,6 +66,14 @@ class MainWindowViewModel { return result.filePaths.length == 1 ? result.filePaths[0] : ""; } + private containsFileExtension(path: string): boolean { + return path.match(/(\.\w{3}$){1}/) != null; + } + + private appendFileExtensionTo(path: string): string { + return path + ".txt"; + } + } class KeyBindingViewModel { @@ -81,7 +92,9 @@ class KeyBindingViewModel { } updateBinding(binding:KeyBindingViewModel, evt: KeyboardEvent): void { - const keyCode = this._keyboard.toKeyCode(evt.key.toUpperCase()); + const keyName = evt.key.toUpperCase().replace(/ARROW/, ""); + // const keyCode = this._keyboard.toKeyCode(evt.key.toUpperCase()); + const keyCode = this._keyboard.toKeyCode(keyName); this.keyboardCode(keyCode); this.keyboardButton(this.getButton(evt)); evt.preventDefault(); From d23a725c0fec75d47bef22c3cd048fd8bc2e809e Mon Sep 17 00:00:00 2001 From: lkomanetz Date: Wed, 15 Jul 2020 13:52:24 -0500 Subject: [PATCH 12/15] Removes a commented line --- UI/XKey/main-view-model.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/UI/XKey/main-view-model.ts b/UI/XKey/main-view-model.ts index b301581..4d99fba 100644 --- a/UI/XKey/main-view-model.ts +++ b/UI/XKey/main-view-model.ts @@ -93,7 +93,6 @@ class KeyBindingViewModel { updateBinding(binding:KeyBindingViewModel, evt: KeyboardEvent): void { const keyName = evt.key.toUpperCase().replace(/ARROW/, ""); - // const keyCode = this._keyboard.toKeyCode(evt.key.toUpperCase()); const keyCode = this._keyboard.toKeyCode(keyName); this.keyboardCode(keyCode); this.keyboardButton(this.getButton(evt)); From b870cd53ba9e6ce779c32763e328175c2df87683 Mon Sep 17 00:00:00 2001 From: lkomanetz Date: Thu, 16 Jul 2020 10:02:52 -0500 Subject: [PATCH 13/15] Fixes bug where enter key was not saving. --- UI/XKey/keyboard/windows-keyboard.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/UI/XKey/keyboard/windows-keyboard.ts b/UI/XKey/keyboard/windows-keyboard.ts index f3f5e38..6a2d9ff 100644 --- a/UI/XKey/keyboard/windows-keyboard.ts +++ b/UI/XKey/keyboard/windows-keyboard.ts @@ -55,6 +55,7 @@ export class WindowsKeyboard implements IKeyboardButton { const keyCode = parseInt(dt[1].innerText, 16); if (keyText === "ESCAPE") specialChars.set(-1, "NULL"); + else if (keyText === "RETURN") specialChars.set(keyCode, "ENTER"); else specialChars.set(keyCode, keyText); } }); From 7360728afc6c6badcdb3d6575f259e08b213fc0a Mon Sep 17 00:00:00 2001 From: lkomanetz Date: Mon, 20 Jul 2020 10:39:52 -0500 Subject: [PATCH 14/15] Refactors button names and IDs to controller types --- UI/XKey/app-info.ts | 3 ++ UI/XKey/controller/controller.ts | 7 ++++ UI/XKey/controller/xbox-controller.ts | 48 +++++++++++++++++++++++++++ UI/XKey/converters.ts | 15 --------- UI/XKey/file-system.ts | 14 ++++---- UI/XKey/key-binding.ts | 27 --------------- UI/XKey/main-view-model.ts | 23 +++++++------ 7 files changed, 77 insertions(+), 60 deletions(-) create mode 100644 UI/XKey/controller/controller.ts create mode 100644 UI/XKey/controller/xbox-controller.ts diff --git a/UI/XKey/app-info.ts b/UI/XKey/app-info.ts index e9220ee..a7e7b9a 100644 --- a/UI/XKey/app-info.ts +++ b/UI/XKey/app-info.ts @@ -1,5 +1,7 @@ import { IKeyboardButton } from "./keyboard/keyboard-button-interface"; import { WindowsKeyboard, EmptyKeyboard } from "./keyboard"; +import { IController } from "./controller/controller"; +import { XboxController } from "./controller/xbox-controller"; export class AppInfo { private _keyboard: IKeyboardButton; @@ -15,5 +17,6 @@ export class AppInfo { } get keyboard(): IKeyboardButton { return this._keyboard; } + get controller(): IController { return new XboxController(); } } \ No newline at end of file diff --git a/UI/XKey/controller/controller.ts b/UI/XKey/controller/controller.ts new file mode 100644 index 0000000..d559cff --- /dev/null +++ b/UI/XKey/controller/controller.ts @@ -0,0 +1,7 @@ +export interface IController { + + getButtonId(buttonName: string): number; + getButtonName(buttonId: number): string; + getButtonList(): Array; + +} \ No newline at end of file diff --git a/UI/XKey/controller/xbox-controller.ts b/UI/XKey/controller/xbox-controller.ts new file mode 100644 index 0000000..230ac3a --- /dev/null +++ b/UI/XKey/controller/xbox-controller.ts @@ -0,0 +1,48 @@ +import { IController } from "./controller"; + +export class XboxController implements IController { + + private _buttonMap: Map; + + constructor() { + this._buttonMap = this.buildButtonMap(); + } + + public getButtonId(buttonName: string): number { + for (const [key, value] of this._buttonMap) { + if (value === buttonName.toUpperCase()) return key; + } + return -1; + } + + public getButtonName(buttonId: number): string { + return this._buttonMap.get(buttonId); + } + + public getButtonList(): Array { + const buttonCollection = []; + for (const [_, value] of this._buttonMap) buttonCollection.push(value); + return buttonCollection; + } + + private buildButtonMap(): Map { + const buttonMap = new Map(); + buttonMap.set(0, "A"); + buttonMap.set(1, "B"); + buttonMap.set(2, "X"); + buttonMap.set(3, "Y"); + buttonMap.set(4, "LeftShoulder"); + buttonMap.set(5, "RightShoulder"); + buttonMap.set(6, "Back"); + buttonMap.set(7, "Menu"); + buttonMap.set(8, "Home"); + buttonMap.set(9, "LeftStick"); + buttonMap.set(10, "RightStick"); + buttonMap.set(11, "DpadRight"); + buttonMap.set(12, "DpadLeft"); + buttonMap.set(13, "DpadUp"); + buttonMap.set(14, "DpadDown"); + return buttonMap; + } + +} \ No newline at end of file diff --git a/UI/XKey/converters.ts b/UI/XKey/converters.ts index 26dd992..555b88e 100644 --- a/UI/XKey/converters.ts +++ b/UI/XKey/converters.ts @@ -1,18 +1,3 @@ -import { GamepadButtons } from "./key-binding"; - -export class GamepadButtonConverter { - - toButtonName(buttonId: number) { - return Object.keys(GamepadButtons).find(k => GamepadButtons[k] === buttonId); - } - - toButtonId(buttonName: string): number { - const enumVal = Object.values(GamepadButtons).find(name => name === buttonName); - return GamepadButtons[enumVal]; - } - -} - export class KeyboardCodeConverter { convert(keyCode: number): string { diff --git a/UI/XKey/file-system.ts b/UI/XKey/file-system.ts index 6967d94..d6e7aca 100644 --- a/UI/XKey/file-system.ts +++ b/UI/XKey/file-system.ts @@ -1,14 +1,14 @@ import * as fs from "fs"; -import { KeyBinding, GamepadButtons } from "./key-binding"; -import { GamepadButtonConverter } from "./converters"; +import { KeyBinding } from "./key-binding"; +import { IController } from "./controller/controller"; export class FileSystem { - private _buttonConverter: GamepadButtonConverter; + private readonly _controller: IController; private _encodingStyle: string; - constructor(buttonConverter: GamepadButtonConverter, encodingStyle: string) { - this._buttonConverter = buttonConverter; + constructor(controller: IController, encodingStyle: string) { + this._controller = controller; this._encodingStyle = encodingStyle; } @@ -19,7 +19,7 @@ export class FileSystem { data.forEach(item => { let buttonInfo = item.split("="); - let button = this._buttonConverter.toButtonName(parseInt(buttonInfo[0])); + let button = this._controller.getButtonName(parseInt(buttonInfo[0])); const keyCode = (buttonInfo[1].trim() === "NULL") ? -1 : parseInt(buttonInfo[1], 16); bindings.push(new KeyBinding(button, keyCode)); }); @@ -45,7 +45,7 @@ export class FileSystem { private toOutputString(binding: KeyBinding): string { const toHex = (n: number) => n.toString(16).toUpperCase(); - const buttonId = this._buttonConverter.toButtonId(binding.gamepadButton); + const buttonId = this._controller.getButtonId(binding.gamepadButton); return `${buttonId}=${binding.keyboardButton === -1 ? 'NULL' : toHex(binding.keyboardButton)}`; } diff --git a/UI/XKey/key-binding.ts b/UI/XKey/key-binding.ts index 1a7c84d..ab7f6fc 100644 --- a/UI/XKey/key-binding.ts +++ b/UI/XKey/key-binding.ts @@ -8,30 +8,3 @@ export class KeyBinding { this.keyboardButton = keyboardButton; } } - -export enum GamepadButtons { - A = 0, - B = 1, - X = 2, - Y = 3, - LeftShoulder = 4, - RightShoulder = 5, - Back = 6, - Menu = 7, - Home = 8, - LeftStick = 9, - RightStick = 10, - DpadRight = 11, - DpadLeft = 12, - DpadUp = 13, - DpadDown = 14 -} - -export function getGamepadButtonList(): Array { - const buttons = []; - for (const member in GamepadButtons) { - const isValueProperty = parseInt(member, 10) >= 0; - if (isValueProperty) buttons.push(GamepadButtons[member]); - } - return buttons; -} \ No newline at end of file diff --git a/UI/XKey/main-view-model.ts b/UI/XKey/main-view-model.ts index 4d99fba..7757308 100644 --- a/UI/XKey/main-view-model.ts +++ b/UI/XKey/main-view-model.ts @@ -2,9 +2,9 @@ import * as ko from "knockout"; import { remote } from "electron"; import { FileSystem } from "./file-system"; import { IKeyboardButton, WindowsKeyboard } from "./keyboard"; -import { KeyBinding, getGamepadButtonList } from "./key-binding"; +import { KeyBinding } from "./key-binding"; import { AppInfo } from "./app-info"; -import { GamepadButtonConverter, KeyboardCodeConverter } from "./converters"; +import { IController } from "./controller/controller"; const dialog = remote.dialog; @@ -12,21 +12,21 @@ class MainWindowViewModel { private fileSystem: FileSystem; private _actionName: any; - private _buttonConverter: GamepadButtonConverter; private _keyboard: IKeyboardButton; + private readonly _controller: IController; private _currentFilePath: string; bindings: any; constructor( fileSystem: FileSystem, - buttonConverter: GamepadButtonConverter, - keyboard: IKeyboardButton + keyboard: IKeyboardButton, + controller: IController ) { + this._controller = controller; this.fileSystem = fileSystem; this.bindings = ko.observableArray([]); this._keyboard = keyboard; - this._buttonConverter = buttonConverter; this._actionName = ko.observable(""); } @@ -43,8 +43,9 @@ class MainWindowViewModel { createBindings() { let bindings: Array = []; - for (const prop in getGamepadButtonList()) { - const button = this._buttonConverter.toButtonName(parseInt(prop)); + for (const prop in this._controller.getButtonList()) { + //const button = this._buttonConverter.toButtonName(parseInt(prop)); + const button = this._controller.getButtonName(parseInt(prop)); bindings.push(new KeyBinding(button, -1)); } this.actionName = "new-bindings-template"; @@ -114,8 +115,8 @@ const appInfo = new AppInfo(); ko.applyBindings( new MainWindowViewModel( - new FileSystem(new GamepadButtonConverter(), "utf8"), - new GamepadButtonConverter(), - appInfo.keyboard + new FileSystem(appInfo.controller, "utf8"), + appInfo.keyboard, + appInfo.controller ) ); \ No newline at end of file From 1910954f1624a9aaec96aa4a46c451d46066800a Mon Sep 17 00:00:00 2001 From: lkomanetz Date: Mon, 20 Jul 2020 11:45:49 -0500 Subject: [PATCH 15/15] Removing unnnecessary import statements. --- UI/XKey/main-view-model.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/XKey/main-view-model.ts b/UI/XKey/main-view-model.ts index 7757308..f674d01 100644 --- a/UI/XKey/main-view-model.ts +++ b/UI/XKey/main-view-model.ts @@ -1,7 +1,7 @@ import * as ko from "knockout"; import { remote } from "electron"; import { FileSystem } from "./file-system"; -import { IKeyboardButton, WindowsKeyboard } from "./keyboard"; +import { IKeyboardButton } from "./keyboard"; import { KeyBinding } from "./key-binding"; import { AppInfo } from "./app-info"; import { IController } from "./controller/controller";