mirror of
https://github.com/danbulant/console-hub
synced 2026-06-21 07:32:05 +00:00
Compare commits
36 commits
0.2.0-ALPH
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73b03f9cf4 | ||
|
|
99d943a41a | ||
|
|
a19487d016 | ||
|
|
39277042a0 | ||
|
|
6533205341 | ||
|
|
4149e2c868 | ||
|
|
38aae4f425 | ||
|
|
e7e4fc4340 | ||
|
|
e39b65860d | ||
|
|
de92e39f34 | ||
|
|
7979ab6f23 | ||
|
|
784997b97d | ||
|
|
4a63e3e4aa | ||
|
|
f7071cc281 | ||
|
|
4488d1421b | ||
|
|
c0d7f3cabf | ||
|
|
ff32867037 | ||
|
|
23b3d9cebc | ||
|
|
3c081d9e30 | ||
|
|
07bd26a9a6 | ||
|
|
8e9015e6ba | ||
|
|
cabd300089 | ||
|
|
59db56981b | ||
|
|
268b15a374 | ||
|
|
fa882c2f97 | ||
|
|
355c050522 | ||
|
|
79fae9716d | ||
|
|
b4cede61ca | ||
|
|
7a934929ef | ||
|
|
f3eea955e2 | ||
|
|
115644037d | ||
|
|
9903a5e044 | ||
|
|
e48963d765 | ||
|
|
4e350d6ff2 | ||
|
|
ed430cc7c5 | ||
|
|
19209efdf0 |
76 changed files with 7159 additions and 1506 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
node_modules
|
||||
24
README.md
24
README.md
|
|
@ -1,10 +1,29 @@
|
|||
# console-hub [](https://travis-ci.org/danbulant/console-hub)
|
||||
Hub application to be controlled with controller (gamepad) through gamepad HTML5 API
|
||||
|
||||
# DEVELOPMENT HALTED
|
||||
|
||||
No further development is planned for console-hub. If you still want to use the code, feel free to do so, but this project won't be maintained anymore.
|
||||
|
||||
---
|
||||
|
||||
Below readme is old and won't work. Also, the current version won't do anything else than just allow switching between accounts on main screen.
|
||||
|
||||
# DEVELOPMENT
|
||||
|
||||
Console hub is currently under heavy rewrite, so it might not be working. Do not report bugs directly - contact me at discord (http://go-dan.tk/discord) instead.
|
||||
|
||||
## Installation
|
||||
Installation can be done both with electron and on normal websites.
|
||||
|
||||
### Electron
|
||||
#### Requirements
|
||||
To run electron version, you must have the following:
|
||||
|
||||
* Java 8 runtime
|
||||
* Node (10+)
|
||||
* Other will be installed automatically via project.json
|
||||
|
||||
If you have installed node.js and electron, you can build the app yourself.
|
||||
For packaging, I personally use (and prefer) [electron-packager](https://github.com/electron-userland/electron-packager).
|
||||
Use all files in the repository (`electron-packager .`)
|
||||
|
|
@ -21,8 +40,9 @@ Because of legal issues (Copyright, cannot upload publicly), fonts and styles ar
|
|||
If you did everything correctly, Console hub now have all required files to run offline.
|
||||
|
||||
## Demo
|
||||
### GitHub
|
||||
A github demo is accesible on [GitHub.io](https://console.danbulant.eu/console-hub/html/index.html), where is available latest development.
|
||||
**No demo is available, as new rewrite uses node's require**
|
||||
### ~~GitHub~~
|
||||
~~A github demo is accesible on [GitHub.io](https://console.danbulant.eu/console-hub/html/index.html), where is available latest development.~~
|
||||
|
||||
## Features
|
||||
For feature/bugs that are currently worked on, please see our (kanban table)[https://trello.com/b/DlwuRuwZ/console-hub]
|
||||
|
|
|
|||
74
controller.js
Normal file
74
controller.js
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
var dualShock = require('./dist/dualshock-controller/src/dualshock');
|
||||
|
||||
|
||||
var controller = dualShock({
|
||||
config: "dualshock4-generic-driver"
|
||||
});
|
||||
controller.on('error', err => console.log(err));
|
||||
|
||||
function setExtras(obj){
|
||||
controller.setExtras(obj);
|
||||
}
|
||||
var connected = false;
|
||||
controller.on('connected', () => {
|
||||
connected = true;
|
||||
});
|
||||
controller.on("disconnected", ()=>{
|
||||
connected = false;
|
||||
})
|
||||
|
||||
function isConnected(){
|
||||
return connected;
|
||||
}
|
||||
//add event handlers:
|
||||
// controller.on('left:move', data => console.log('left Moved: ' + data.x + ' | ' + data.y));
|
||||
|
||||
// controller.on('right:move', data => console.log('right Moved: ' + data.x + ' | ' + data.y));
|
||||
|
||||
// controller.on('square:press', () => console.log('square press'));
|
||||
|
||||
// controller.on('square:release', () => console.log('square release'));
|
||||
|
||||
//sixasis motion events:
|
||||
//the object returned from each of the movement events is as follows:
|
||||
//{
|
||||
// direction : values can be: 1 for right, forward and up. 2 for left, backwards and down.
|
||||
// value : values will be from 0 to 120 for directions right, forward and up and from 0 to -120 for left, backwards and down.
|
||||
//}
|
||||
|
||||
function on(event, handler){
|
||||
controller.on(event, handler);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
on,
|
||||
isConnected,
|
||||
setExtras
|
||||
}
|
||||
// controller.on('touchpad:x1:active', () => console.log('touchpad one finger active'));
|
||||
|
||||
// controller.on('touchpad:x2:active', () => console.log('touchpad two fingers active'));
|
||||
|
||||
// controller.on('touchpad:x2:inactive', () => console.log('touchpad back to single finger'));
|
||||
|
||||
// controller.on('touchpad:x1', data => console.log('touchpad x1:', data.x, data.y));
|
||||
|
||||
// controller.on('touchpad:x2', data => console.log('touchpad x2:', data.x, data.y));
|
||||
|
||||
|
||||
// //right-left movement
|
||||
// controller.on('rightLeft:motion', data => console.log(data));
|
||||
|
||||
// //forward-back movement
|
||||
// controller.on('forwardBackward:motion', data => console.log(data));
|
||||
|
||||
// //up-down movement
|
||||
// controller.on('upDown:motion', data => console.log(data));
|
||||
|
||||
// //controller status
|
||||
// //as of version 0.6.2 you can get the battery %, if the controller is connected and if the controller is charging
|
||||
// controller.on('battery:change', data => console.log(data));
|
||||
|
||||
// controller.on('connection:change', data => console.log(data));
|
||||
|
||||
// controller.on('charging:change', data => console.log(data));
|
||||
2
dist/css/styles.min.css
vendored
Normal file
2
dist/css/styles.min.css
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
@import url("https://fonts.googleapis.com/css?family=Open+Sans&display=swap");@font-face{font-family:'Gilroy-bold';src:url("../../dist/fonts/Gilroy-ExtraBold.otf")}body{margin:0;padding:0;background-color:#0e6cc4;overflow-x:hidden;overflow-y:hidden}.title--main{margin:0;padding:0;position:fixed;left:45px;top:100px;font-size:4rem;color:white;font-family:'Gilroy-bold'}.view{width:100%}.title--description{margin:0;padding:0;position:fixed;left:80px;top:175px;font-size:1.3rem;color:white;font-family:'Open Sans', sans-serif}.title--main.middle,.title--description.middle{left:50%;top:50%}.user-cards-list{z-index:99;width:100%;height:30vh;margin-top:30vh;display:-webkit-box;display:-ms-flexbox;display:flex}.user-cards-list .card{z-index:99;margin:5px;font-family:'Open Sans', sans-serif;width:30vh;height:30vh;background-size:30vh 30vh;font-size:2rem;color:white;border:5px solid transparent}.user-cards-list .card .name{margin-top:-15px;padding-left:5px;font-size:43px}.user-cards-list .card img{height:100%;width:100%}.user-cards-list .card p{margin:0}.user-cards-list .card.selected{border:5px solid white}.user-cards-list .card:first-child{margin-left:auto}.user-cards-list .card:last-child{margin-right:auto}.box{z-index:1;position:fixed;top:0;-webkit-transform:rotate(80deg);transform:rotate(80deg);left:0}.wave{position:fixed;top:0;left:0;opacity:.4;position:absolute;top:3%;left:10%;background:#0af;width:3000px;height:2600px;margin-left:-150px;margin-top:-250px;-webkit-transform-origin:50% 48%;transform-origin:50% 48%;border-radius:43%;-webkit-animation:drift 9000ms infinite linear;animation:drift 9000ms infinite linear}.wave.-three{-webkit-animation:drift 8500ms infinite linear;animation:drift 8500ms infinite linear;position:fixed;background-color:#77daff}.wave.-two{-webkit-animation:drift 8000ms infinite linear;animation:drift 8000ms infinite linear;opacity:.1;background:black;position:fixed}.box:after{content:'';display:block;left:0;top:0;width:100%;height:100%;z-index:11;-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0)}@-webkit-keyframes drift{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}from{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes drift{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}from{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}
|
||||
/*# sourceMappingURL=styles.min.css.map */
|
||||
9
dist/css/styles.min.css.map
vendored
Normal file
9
dist/css/styles.min.css.map
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"version": 3,
|
||||
"mappings": "AAQA,OAAO,CAAC,qEAAI,CACZ,UAAU,CACR,WAAW,CAAE,aAAa,CAC1B,GAAG,CAAE,4CAA4C,CAGnD,AAAA,IAAI,AAAC,CACH,MAAM,CAAE,CAAC,CACT,OAAO,CAAE,CAAC,CACX,gBAAgB,CAAE,OAAO,CACzB,UAAU,CAAC,MAAM,CACjB,UAAU,CAAC,MAAM,CACjB,AAED,AAAA,YAAY,AAAC,CACX,MAAM,CAAE,CAAC,CACT,OAAO,CAAE,CAAC,CACV,QAAQ,CAAE,KAAK,CACf,IAAI,CAAE,IAAI,CACV,GAAG,CAAE,KAAK,CACV,SAAS,CAAE,IAAI,CACf,KAAK,CAAE,KAAK,CACZ,WAAW,CAAE,aAAa,CAC3B,AACD,AAAA,KAAK,AAAC,CACJ,KAAK,CAAE,IAAI,CACZ,AACD,AAAA,mBAAmB,AAAC,CAClB,MAAM,CAAE,CAAC,CACT,OAAO,CAAE,CAAC,CACV,QAAQ,CAAE,KAAK,CACf,IAAI,CAAE,IAAI,CACV,GAAG,CAAE,KAAK,CACV,SAAS,CAAE,MAAM,CACjB,KAAK,CAAE,KAAK,CACZ,WAAW,CAAE,uBAAuB,CACrC,AAED,AAAA,YAAY,AAAA,OAAO,CAAE,mBAAmB,AAAA,OAAO,AAAC,CAC9C,IAAI,CAAE,GAAG,CACT,GAAG,CAAE,GAAG,CACT,AAED,AAAA,gBAAgB,AAAC,CACf,OAAO,CAAE,EAAE,CACX,KAAK,CAAE,IAAI,CACX,MAAM,CAAE,IAAI,CACZ,UAAU,CAAE,IAAI,CAChB,OAAO,CAAE,IAAI,CAmCd,AAxCD,AAME,gBANc,CAMd,KAAK,AAAC,CACJ,OAAO,CAAE,EAAE,CACX,MAAM,CAAE,GAAG,CACX,WAAW,CAAE,uBAAuB,CACpC,KAAK,CAAE,IAAI,CACX,MAAM,CAAE,IAAI,CACZ,eAAe,CAAE,SAAS,CAC1B,SAAS,CAAE,IAAI,CACf,KAAK,CAAE,KAAK,CACZ,MAAM,CAAE,qBAAqB,CAa9B,AA5BH,AAgBI,gBAhBY,CAMd,KAAK,CAUH,KAAK,AAAC,CACJ,UAAU,CAAE,KAAK,CACjB,YAAY,CAAE,GAAG,CACjB,SAAS,CAAE,IAAI,CAChB,AApBL,AAqBI,gBArBY,CAMd,KAAK,CAeH,GAAG,AAAC,CACF,MAAM,CAAE,IAAI,CACZ,KAAK,CAAE,IAAI,CACZ,AAxBL,AAyBI,gBAzBY,CAMd,KAAK,CAmBH,CAAC,AAAC,CACA,MAAM,CAAE,CAAC,CACV,AA3BL,AA6BE,gBA7Bc,CA6Bd,KAAK,AAAA,SAAS,AAAC,CAGb,MAAM,CAAE,eAAe,CACxB,AAjCH,AAkCE,gBAlCc,CAkCd,KAAK,AAAA,YAAY,AAAC,CAChB,WAAW,CAAE,IAAI,CAClB,AApCH,AAqCE,gBArCc,CAqCd,KAAK,AAAA,WAAW,AAAC,CACf,YAAY,CAAE,IAAI,CACnB,AAGH,AAAA,IAAI,AAAC,CACH,OAAO,CAAE,CAAC,CACX,QAAQ,CAAE,KAAK,CACf,GAAG,CAAE,CAAC,CACN,SAAS,CAAE,aAAa,CACxB,IAAI,CAAE,CAAC,CACP,AAED,AAAA,KAAK,AAAC,CACL,QAAQ,CAAE,KAAK,CACf,GAAG,CAAE,CAAC,CACN,IAAI,CAAE,CAAC,CACN,OAAO,CAAE,EAAE,CACX,QAAQ,CAAE,QAAQ,CAClB,GAAG,CAAE,EAAE,CACP,IAAI,CAAE,GAAG,CACT,UAAU,CAAE,IAAI,CAChB,KAAK,CAAE,MAAM,CACb,MAAM,CAAE,MAAM,CACd,WAAW,CAAE,MAAM,CACnB,UAAU,CAAE,MAAM,CAClB,gBAAgB,CAAE,OAAO,CACzB,aAAa,CAAE,GAAG,CAClB,SAAS,CAAE,4BAA4B,CACxC,AAED,AAAA,KAAK,AAAA,OAAO,AAAC,CACX,SAAS,CAAE,4BAA4B,CACxC,QAAQ,CAAE,KAAK,CACf,gBAAgB,CAAE,OAAO,CACzB,AAED,AAAA,KAAK,AAAA,KAAK,AAAC,CACT,SAAS,CAAE,4BAA4B,CACvC,OAAO,CAAE,EAAE,CACX,UAAU,CAAE,KAAK,CAClB,QAAQ,CAAE,KAAK,CACf,AAED,AAAA,IAAI,AAAA,MAAM,AAAC,CACT,OAAO,CAAE,EAAE,CACX,OAAO,CAAE,KAAK,CACd,IAAI,CAAE,CAAC,CACP,GAAG,CAAE,CAAC,CACN,KAAK,CAAE,IAAI,CACX,MAAM,CAAE,IAAI,CACZ,OAAO,CAAE,EAAE,CACX,SAAS,CAAE,oBAAoB,CAChC,AAED,UAAU,CAAV,KAAU,CACR,IAAI,CAAG,SAAS,CAAE,YAAY,CAC9B,IAAI,CAAG,SAAS,CAAE,cAAc",
|
||||
"sources": [
|
||||
"../../html/views/styles.scss"
|
||||
],
|
||||
"names": [],
|
||||
"file": "styles.min.css"
|
||||
}
|
||||
9
dist/dualshock-controller/.editorconfig
vendored
Normal file
9
dist/dualshock-controller/.editorconfig
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
6
dist/dualshock-controller/.gitignore
vendored
Normal file
6
dist/dualshock-controller/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
node_modules
|
||||
*.log
|
||||
coverage
|
||||
*.iml
|
||||
*.idea
|
||||
.DS_Store
|
||||
6
dist/dualshock-controller/.travis.yml
vendored
Normal file
6
dist/dualshock-controller/.travis.yml
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- "6.9.2"
|
||||
before_script:
|
||||
install: echo "node-hid installation will fail in travis. Overiding `npm install`"
|
||||
compiler: clang
|
||||
194
dist/dualshock-controller/README.md
vendored
Normal file
194
dist/dualshock-controller/README.md
vendored
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
node-dualshock-controller
|
||||
=========================
|
||||
[](https://travis-ci.org/rdepena/node-dualshock-controller) [](https://codeclimate.com/github/rdepena/node-dualshock-controller)
|
||||
|
||||
`dualshock-controller` Eventing API layer over HID for the Sony DualShock 3 and DualShock 4 controllers
|
||||
|
||||
## Installation:
|
||||
|
||||
#### OSX/Windows:
|
||||
|
||||
```bash
|
||||
npm install dualshock-controller
|
||||
```
|
||||
#### Linux:
|
||||
|
||||
Review the [Linux support](#linux-support) section.
|
||||
|
||||
## Using the DualShock library
|
||||
|
||||
`Important: THE CONTROLLER WILL NOT SEND ANY DATA IF YOU DO NOT PRESS THE PS BUTTON.`
|
||||
|
||||
Also, to use the touchpad, rumble and LED capabilities of the controller you
|
||||
must connect the controller to your computer using a micro-USB cable.
|
||||
|
||||
~~~~ javascript
|
||||
var dualShock = require('dualshock-controller');
|
||||
|
||||
//pass options to init the controller.
|
||||
var controller = dualShock(
|
||||
{
|
||||
//you can use a ds4 by uncommenting this line.
|
||||
//config: "dualshock4-generic-driver",
|
||||
//if the above configuration doesn't work for you,
|
||||
//try uncommenting the following line instead.
|
||||
//config: "dualshock4-alternate-driver"
|
||||
//if using ds4 comment this line.
|
||||
config: "dualShock3",
|
||||
//smooths the output from the acelerometers (moving averages) defaults to true
|
||||
accelerometerSmoothing: true,
|
||||
//smooths the output from the analog sticks (moving averages) defaults to false
|
||||
analogStickSmoothing: false
|
||||
});
|
||||
|
||||
//make sure you add an error event handler
|
||||
controller.on('error', err => console.log(err));
|
||||
|
||||
//DualShock 4 control rumble and light settings for the controller
|
||||
controller.setExtras({
|
||||
rumbleLeft: 0, // 0-255 (Rumble left intensity)
|
||||
rumbleRight: 0, // 0-255 (Rumble right intensity)
|
||||
red: 0, // 0-255 (Red intensity)
|
||||
green: 75, // 0-255 (Blue intensity)
|
||||
blue: 225, // 0-255 (Green intensity)
|
||||
flashOn: 40, // 0-255 (Flash on time)
|
||||
flashOff: 10 // 0-255 (Flash off time)
|
||||
});
|
||||
|
||||
//DualShock 3 control rumble and light settings for the controller
|
||||
controller.setExtras({
|
||||
rumbleLeft: 0, // 0-1 (Rumble left on/off)
|
||||
rumbleRight: 0, // 0-255 (Rumble right intensity)
|
||||
led: 2 // 2 | 4 | 8 | 16 (Leds 1-4 on/off, bitmasked)
|
||||
});
|
||||
|
||||
//add event handlers:
|
||||
controller.on('left:move', data => console.log('left Moved: ' + data.x + ' | ' + data.y));
|
||||
|
||||
controller.on('right:move', data => console.log('right Moved: ' + data.x + ' | ' + data.y));
|
||||
|
||||
controller.on('connected', () => console.log('connected'));
|
||||
|
||||
controller.on('square:press', ()=> console.log('square press'));
|
||||
|
||||
controller.on('square:release', () => console.log('square release'));
|
||||
|
||||
//sixasis motion events:
|
||||
//the object returned from each of the movement events is as follows:
|
||||
//{
|
||||
// direction : values can be: 1 for right, forward and up. 2 for left, backwards and down.
|
||||
// value : values will be from 0 to 120 for directions right, forward and up and from 0 to -120 for left, backwards and down.
|
||||
//}
|
||||
|
||||
//DualShock 4 TouchPad
|
||||
//finger 1 is x1 finger 2 is x2
|
||||
controller.on('touchpad:x1:active', () => console.log('touchpad one finger active'));
|
||||
|
||||
controller.on('touchpad:x2:active', () => console.log('touchpad two fingers active'));
|
||||
|
||||
controller.on('touchpad:x2:inactive', () => console.log('touchpad back to single finger'));
|
||||
|
||||
controller.on('touchpad:x1', data => console.log('touchpad x1:', data.x, data.y));
|
||||
|
||||
controller.on('touchpad:x2', data => console.log('touchpad x2:', data.x, data.y));
|
||||
|
||||
|
||||
//right-left movement
|
||||
controller.on('rightLeft:motion', data => console.log(data));
|
||||
|
||||
//forward-back movement
|
||||
controller.on('forwardBackward:motion', data => console.log(data));
|
||||
|
||||
//up-down movement
|
||||
controller.on('upDown:motion', data => console.log(data));
|
||||
|
||||
//controller status
|
||||
//as of version 0.6.2 you can get the battery %, if the controller is connected and if the controller is charging
|
||||
controller.on('battery:change', data => console.log(data));
|
||||
|
||||
controller.on('connection:change', data => console.log(data));
|
||||
|
||||
controller.on('charging:change', data => console.log(data));
|
||||
|
||||
~~~~
|
||||
|
||||
## <a name="linux-support"></a> Linux support:
|
||||
|
||||
In order to provide Rumble/Gyro and LED support for all platforms the linux specific joystick implementation has been removed. This means you will need to:
|
||||
|
||||
* [Install node-hid build requirements](#node-hid-build)
|
||||
* [Install node-hid with hidraw support](#node-hid-hidraw)
|
||||
* [create udev rules](#create-udev-rules)
|
||||
|
||||
#### <a name="node-hid-build"></a> Install node-hid build requirements
|
||||
|
||||
To build node-hid you will need to install:
|
||||
|
||||
* libudev-dev
|
||||
* libusb-1.0-0
|
||||
* libusb-1.0-0-dev
|
||||
* build-essential
|
||||
* git
|
||||
* node-gyp
|
||||
* node-pre-gyp
|
||||
|
||||
Using apt-get:
|
||||
|
||||
```bash
|
||||
sudo apt-get install libudev-dev libusb-1.0-0 libusb-1.0-0-dev build-essential git
|
||||
```
|
||||
|
||||
```bash
|
||||
npm install -g node-gyp node-pre-gyp
|
||||
```
|
||||
|
||||
#### <a name="node-hid-hidraw"></a> Install node-hid with hidraw support
|
||||
|
||||
Once you have run the installation scripts above you can install the node-dualshock module, then replace the installed node-hid with hidraw support enabled node-hid:
|
||||
|
||||
```bash
|
||||
npm install dualshock-controller
|
||||
```
|
||||
|
||||
```bash
|
||||
npm install node-hid --driver=hidraw --build-from-source
|
||||
```
|
||||
|
||||
#### <a name="create-udev-rules"></a> Create udev rules
|
||||
|
||||
You will need to create a udev rule to be able to access the hid stream as a non root user.
|
||||
|
||||
Write the following file in `/etc/udev/rules.d/61-dualshock.rules`
|
||||
|
||||
```
|
||||
SUBSYSTEM=="input", GROUP="input", MODE="0666"
|
||||
SUBSYSTEM=="usb", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="0268", MODE:="666", GROUP="plugdev"
|
||||
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0664", GROUP="plugdev"
|
||||
|
||||
SUBSYSTEM=="input", GROUP="input", MODE="0666"
|
||||
SUBSYSTEM=="usb", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="05c4", MODE:="666", GROUP="plugdev"
|
||||
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0664", GROUP="plugdev"
|
||||
```
|
||||
|
||||
Reload the rules `sudo udevadm control --reload-rules`, then disconnect/connect the controller.
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Ricardo de Pena
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
35
dist/dualshock-controller/controllerConfigurations/README.md
vendored
Normal file
35
dist/dualshock-controller/controllerConfigurations/README.md
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
These json files are used to map the controller:
|
||||
=========================
|
||||
|
||||
## To add a new controller:
|
||||
Use DeviceDiscoveryHelp.js in the examples folder, using node-hid you can obtain the values you need to create a controller.json file and leverage the library to wire it up.
|
||||
|
||||
### To connect the right controller
|
||||
the vendorId and the productId need to be set to the right values, you can use node-hid to determine what these are.
|
||||
|
||||
### Analogs are mapped as:
|
||||
~~~~ js
|
||||
"analogSticks" : [
|
||||
{
|
||||
"name" : "left",
|
||||
"x" : 7,
|
||||
"y" : 6
|
||||
},
|
||||
{
|
||||
"name" : "right",
|
||||
"x" : 9,
|
||||
"y" : 8
|
||||
}
|
||||
]
|
||||
|
||||
### Buttons are usually grouped by a block but should be added as:
|
||||
|
||||
~~~~ js
|
||||
"buttons" : [
|
||||
{
|
||||
"name": name of the button used for events,
|
||||
"buttonBlock": int representing the button block,
|
||||
"buttonValue": bit value,
|
||||
"analogPin" : int representing the pin used for analog.
|
||||
},
|
||||
]
|
||||
244
dist/dualshock-controller/controllerConfigurations/dualShock3.json
vendored
Normal file
244
dist/dualshock-controller/controllerConfigurations/dualShock3.json
vendored
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
{
|
||||
"vendorId" : 1356,
|
||||
"productId" : 616,
|
||||
"analogSticks" : [
|
||||
{
|
||||
"name" : "left",
|
||||
"x" : 6,
|
||||
"y" : 7,
|
||||
"joystickXNumber" : 0,
|
||||
"joystickYNumber" : 1
|
||||
},
|
||||
{
|
||||
"name" : "right",
|
||||
"x" : 8,
|
||||
"y" : 9,
|
||||
"joystickXNumber" : 2,
|
||||
"joystickYNumber" : 3
|
||||
}
|
||||
],
|
||||
"buttons" : [
|
||||
{
|
||||
"name": "l2",
|
||||
"buttonBlock": 3,
|
||||
"buttonValue": "0x01",
|
||||
"analogPin" : 18,
|
||||
"joystickNumber": 8
|
||||
},
|
||||
{
|
||||
"name": "r2",
|
||||
"buttonBlock": 3,
|
||||
"buttonValue": "0x02",
|
||||
"analogPin" : 19,
|
||||
"joystickNumber" : 9
|
||||
},
|
||||
{
|
||||
"name": "l1",
|
||||
"buttonBlock": 3,
|
||||
"buttonValue": "0x04",
|
||||
"analogPin" : 20,
|
||||
"joystickNumber" : 10
|
||||
},
|
||||
{
|
||||
"name":"r1",
|
||||
"buttonBlock": 3,
|
||||
"buttonValue": "0x08",
|
||||
"analogPin" : 21,
|
||||
"joystickNumber" : 11
|
||||
},
|
||||
{
|
||||
"name": "triangle",
|
||||
"buttonBlock": 3,
|
||||
"buttonValue": "0x10",
|
||||
"analogPin" : 22,
|
||||
"joystickNumber" : 12
|
||||
},
|
||||
{
|
||||
"name": "circle",
|
||||
"buttonBlock": 3,
|
||||
"buttonValue": "0x20",
|
||||
"analogPin" : 23,
|
||||
"joystickNumber" : 13
|
||||
},
|
||||
{
|
||||
"name": "x",
|
||||
"buttonBlock": 3,
|
||||
"buttonValue": "0x40",
|
||||
"analogPin" : 24,
|
||||
"joystickNumber" : 14
|
||||
},
|
||||
{
|
||||
"name": "square",
|
||||
"buttonBlock": 3,
|
||||
"buttonValue": "0x80",
|
||||
"analogPin": 25,
|
||||
"joystickNumber" : 15
|
||||
},
|
||||
{
|
||||
"name": "select",
|
||||
"buttonBlock": 2,
|
||||
"buttonValue": "0x1",
|
||||
"joystickNumber" : 0
|
||||
},
|
||||
{
|
||||
"name": "leftAnalogBump",
|
||||
"buttonBlock": 2,
|
||||
"buttonValue": "0x2",
|
||||
"joystickNumber" : 1
|
||||
},
|
||||
{
|
||||
"name": "rightAnalogBump",
|
||||
"buttonBlock": 2,
|
||||
"buttonValue": "0x4",
|
||||
"joystickNumber" : 2
|
||||
},
|
||||
{
|
||||
"name": "start",
|
||||
"buttonBlock": 2,
|
||||
"buttonValue": "0x08",
|
||||
"joystickNumber" : 3
|
||||
},
|
||||
{
|
||||
"name": "dpadUp",
|
||||
"buttonBlock": 2,
|
||||
"buttonValue": "0x10",
|
||||
"analogPin": 14,
|
||||
"joystickNumber" : 4
|
||||
},
|
||||
{
|
||||
"name": "dpadRight",
|
||||
"buttonBlock": 2,
|
||||
"buttonValue": "0x20",
|
||||
"analogPin": 15,
|
||||
"joystickNumber" : 5
|
||||
},
|
||||
{
|
||||
"name": "dpadDown",
|
||||
"buttonBlock": 2,
|
||||
"buttonValue" : "0x40",
|
||||
"analogPin" : 16,
|
||||
"joystickNumber" : 6
|
||||
},
|
||||
{
|
||||
"name": "dpadLeft",
|
||||
"buttonBlock": 2,
|
||||
"buttonValue": "0x80",
|
||||
"analogPin": 17,
|
||||
"joystickNumber" : 7
|
||||
},
|
||||
{
|
||||
"name": "psxButton",
|
||||
"buttonBlock":4,
|
||||
"buttonValue": "0x01",
|
||||
"joystickNumber" : 16
|
||||
}
|
||||
],
|
||||
"motionInputs": [
|
||||
{
|
||||
"name" : "rightLeft",
|
||||
"directionPin" : 41,
|
||||
"valuePin" : 42
|
||||
},
|
||||
{
|
||||
"name" : "forwardBackward",
|
||||
"directionPin" : 43,
|
||||
"valuePin" : 44
|
||||
},
|
||||
{
|
||||
"name" : "upDown",
|
||||
"directionPin" : 45,
|
||||
"valuePin" : 46
|
||||
},
|
||||
{
|
||||
"name" : "yaw",
|
||||
"directionPin" : 47,
|
||||
"valuePin" : 48
|
||||
}
|
||||
],
|
||||
"status": [
|
||||
{
|
||||
"name" : "charging",
|
||||
"pin" : 29,
|
||||
"states" : [
|
||||
{
|
||||
"value" : 0,
|
||||
"state" : "Charging"
|
||||
},
|
||||
{
|
||||
"value" : 2,
|
||||
"state" : "Charging"
|
||||
},
|
||||
{
|
||||
"value" : 3,
|
||||
"state" : "Not Charging"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "battery",
|
||||
"pin" : 30,
|
||||
"states" : [
|
||||
{
|
||||
"value" : 238,
|
||||
"state" : "Charging"
|
||||
},
|
||||
{
|
||||
"value" : 0,
|
||||
"state" : "No charge"
|
||||
},
|
||||
{
|
||||
"value" : 1,
|
||||
"state" : "20%"
|
||||
},
|
||||
{
|
||||
"value" : 2,
|
||||
"state" : "40%"
|
||||
},
|
||||
{
|
||||
"value" : 3,
|
||||
"state" : "60%"
|
||||
},
|
||||
{
|
||||
"value" : 4,
|
||||
"state" : "80%"
|
||||
},
|
||||
{
|
||||
"value" : 5,
|
||||
"state" : "100%"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "connection",
|
||||
"pin" : 31,
|
||||
"states" : [
|
||||
{
|
||||
"value" : 18,
|
||||
"state" : "Usb"
|
||||
},
|
||||
{
|
||||
"value" : 22,
|
||||
"state" : "Bluetooth"
|
||||
},
|
||||
{
|
||||
"value" : 20,
|
||||
"state" : "Rumbling"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"output": {
|
||||
"defaultBuffer":[
|
||||
1, 0, 254, 0, 254, 0, 0, 0, 0, 0, 0,
|
||||
255, 39, 16, 0, 50, 255, 39, 16, 0, 50, 255,
|
||||
39, 16, 0, 50, 255, 39, 16, 0, 50, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0
|
||||
],
|
||||
"indexes": {
|
||||
"rumbleLeft": 3,
|
||||
"rumbleRight": 5,
|
||||
"led": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
177
dist/dualshock-controller/controllerConfigurations/dualShock4-alternate-driver.json
vendored
Normal file
177
dist/dualshock-controller/controllerConfigurations/dualShock4-alternate-driver.json
vendored
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
{
|
||||
"vendorId" : 1356,
|
||||
"productId" : 2508,
|
||||
"analogSticks" : [
|
||||
{
|
||||
"name" : "left",
|
||||
"x" : 1,
|
||||
"y" : 2
|
||||
},
|
||||
{
|
||||
"name" : "right",
|
||||
"x" : 3,
|
||||
"y" : 4
|
||||
}
|
||||
],
|
||||
"buttons" : [
|
||||
{
|
||||
"name": "l2",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x04",
|
||||
"analogPin" : 8
|
||||
},
|
||||
{
|
||||
"name": "r2",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x08",
|
||||
"analogPin" : 9
|
||||
},
|
||||
{
|
||||
"name": "l1",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x01"
|
||||
},
|
||||
{
|
||||
"name": "r1",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x02"
|
||||
},
|
||||
{
|
||||
"name": "leftAnalogBump",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x04"
|
||||
},
|
||||
{
|
||||
"name": "rightAnalogBump",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x08"
|
||||
},
|
||||
{
|
||||
"name": "psxButton",
|
||||
"buttonBlock": 7,
|
||||
"buttonValue": "0x01"
|
||||
},
|
||||
{
|
||||
"name": "touchPad",
|
||||
"buttonBlock": 7,
|
||||
"buttonValue": "0x02"
|
||||
},
|
||||
{
|
||||
"name": "square",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x10"
|
||||
},
|
||||
{
|
||||
"name": "triangle",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x80"
|
||||
},
|
||||
{
|
||||
"name": "circle",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x40"
|
||||
},
|
||||
{
|
||||
"name": "x",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x20"
|
||||
},
|
||||
{
|
||||
"name": "dpadUp",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x00",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadUpRight",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x01",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadRight",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x02",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadDownRight",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x03",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadDown",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue" : "0x04",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadDownLeft",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue" : "0x05",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadLeft",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x06",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadUpLeft",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x07",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "share",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x10"
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x20"
|
||||
},
|
||||
{
|
||||
"name": "leftStick",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x40"
|
||||
},
|
||||
{
|
||||
"name": "rightStick",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x80"
|
||||
}
|
||||
],
|
||||
"motionInputs" : [],
|
||||
"status" : [],
|
||||
"output": {
|
||||
"defaultBuffer":[
|
||||
5,255,4,0,0,0,0,0,0,0,0
|
||||
],
|
||||
"indexes": {
|
||||
"rumbleLeft": 4,
|
||||
"rumbleRight": 5,
|
||||
"red": 6,
|
||||
"green": 7,
|
||||
"blue": 8,
|
||||
"flashOn": 9,
|
||||
"flashOff": 10
|
||||
}
|
||||
},
|
||||
"touchPad": [{
|
||||
"name":"x1",
|
||||
"activePin": 35,
|
||||
"dataPinA": 37,
|
||||
"dataPinB": 36,
|
||||
"dataPinC": 38
|
||||
},{
|
||||
"name": "x2",
|
||||
"activePin": 39,
|
||||
"dataPinA": 41,
|
||||
"dataPinB": 40,
|
||||
"dataPinC": 42
|
||||
}]
|
||||
}
|
||||
177
dist/dualshock-controller/controllerConfigurations/dualShock4-generic-driver.json
vendored
Normal file
177
dist/dualshock-controller/controllerConfigurations/dualShock4-generic-driver.json
vendored
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
{
|
||||
"vendorId" : 1356,
|
||||
"productId" : 1476,
|
||||
"analogSticks" : [
|
||||
{
|
||||
"name" : "left",
|
||||
"x" : 1,
|
||||
"y" : 2
|
||||
},
|
||||
{
|
||||
"name" : "right",
|
||||
"x" : 3,
|
||||
"y" : 4
|
||||
}
|
||||
],
|
||||
"buttons" : [
|
||||
{
|
||||
"name": "l2",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x04",
|
||||
"analogPin" : 8
|
||||
},
|
||||
{
|
||||
"name": "r2",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x08",
|
||||
"analogPin" : 9
|
||||
},
|
||||
{
|
||||
"name": "l1",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x01"
|
||||
},
|
||||
{
|
||||
"name": "r1",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x02"
|
||||
},
|
||||
{
|
||||
"name": "leftAnalogBump",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x04"
|
||||
},
|
||||
{
|
||||
"name": "rightAnalogBump",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x08"
|
||||
},
|
||||
{
|
||||
"name": "psxButton",
|
||||
"buttonBlock": 7,
|
||||
"buttonValue": "0x01"
|
||||
},
|
||||
{
|
||||
"name": "touchPad",
|
||||
"buttonBlock": 7,
|
||||
"buttonValue": "0x02"
|
||||
},
|
||||
{
|
||||
"name": "square",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x10"
|
||||
},
|
||||
{
|
||||
"name": "triangle",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x80"
|
||||
},
|
||||
{
|
||||
"name": "circle",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x40"
|
||||
},
|
||||
{
|
||||
"name": "x",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x20"
|
||||
},
|
||||
{
|
||||
"name": "dpadUp",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x00",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadUpRight",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x01",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadRight",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x02",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadDownRight",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x03",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadDown",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue" : "0x04",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadDownLeft",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue" : "0x05",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadLeft",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x06",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadUpLeft",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x07",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "share",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x10"
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x20"
|
||||
},
|
||||
{
|
||||
"name": "leftStick",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x40"
|
||||
},
|
||||
{
|
||||
"name": "rightStick",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x80"
|
||||
}
|
||||
],
|
||||
"motionInputs" : [],
|
||||
"status" : [],
|
||||
"output": {
|
||||
"defaultBuffer":[
|
||||
5,255,4,0,0,0,0,0,0,0,0
|
||||
],
|
||||
"indexes": {
|
||||
"rumbleLeft": 4,
|
||||
"rumbleRight": 5,
|
||||
"red": 6,
|
||||
"green": 7,
|
||||
"blue": 8,
|
||||
"flashOn": 9,
|
||||
"flashOff": 10
|
||||
}
|
||||
},
|
||||
"touchPad": [{
|
||||
"name":"x1",
|
||||
"activePin": 35,
|
||||
"dataPinA": 37,
|
||||
"dataPinB": 36,
|
||||
"dataPinC": 38
|
||||
},{
|
||||
"name": "x2",
|
||||
"activePin": 39,
|
||||
"dataPinA": 41,
|
||||
"dataPinB": 40,
|
||||
"dataPinC": 42
|
||||
}]
|
||||
}
|
||||
195
dist/dualshock-controller/controllerConfigurations/dualShock4.json
vendored
Normal file
195
dist/dualshock-controller/controllerConfigurations/dualShock4.json
vendored
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
{
|
||||
"vendorId" : 1356,
|
||||
"productId" : 1476,
|
||||
"analogSticks" : [
|
||||
{
|
||||
"name" : "left",
|
||||
"x" : 1,
|
||||
"y" : 2,
|
||||
"joystickXNumber" : 0,
|
||||
"joystickYNumber" : 1
|
||||
},
|
||||
{
|
||||
"name" : "right",
|
||||
"x" : 3,
|
||||
"y" : 4,
|
||||
"joystickXNumber" : 2,
|
||||
"joystickYNumber" : 5
|
||||
},
|
||||
{
|
||||
"name" : "l2Analog",
|
||||
"x" : 100,
|
||||
"joystickXNumber" : 3
|
||||
},
|
||||
{
|
||||
"name" : "r2Analog",
|
||||
"x" : 101,
|
||||
"joystickXNumber" : 4
|
||||
}
|
||||
],
|
||||
"buttons" : [
|
||||
{
|
||||
"name": "l2",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x04",
|
||||
"analogPin" : 8,
|
||||
"joystickNumber": 6
|
||||
},
|
||||
{
|
||||
"name": "r2",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x08",
|
||||
"analogPin" : 9,
|
||||
"joystickNumber": 7
|
||||
},
|
||||
{
|
||||
"name": "l1",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x01",
|
||||
"joystickNumber": 4
|
||||
},
|
||||
{
|
||||
"name": "r1",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x02",
|
||||
"joystickNumber": 5
|
||||
},
|
||||
{
|
||||
"name": "psxButton",
|
||||
"buttonBlock": 7,
|
||||
"buttonValue": "0x01",
|
||||
"joystickNumber": 12
|
||||
},
|
||||
{
|
||||
"name": "touchPad",
|
||||
"buttonBlock": 7,
|
||||
"buttonValue": "0x02",
|
||||
"joystickNumber": 13
|
||||
},
|
||||
{
|
||||
"name": "square",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x10",
|
||||
"joystickNumber": 0
|
||||
},
|
||||
{
|
||||
"name": "triangle",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x80",
|
||||
"joystickNumber": 3
|
||||
},
|
||||
{
|
||||
"name": "circle",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x40",
|
||||
"joystickNumber": 2
|
||||
},
|
||||
{
|
||||
"name": "x",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x20",
|
||||
"joystickNumber": 1
|
||||
},
|
||||
{
|
||||
"name": "dpadUp",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x00",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadUpRight",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x01",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadRight",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x02",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadDownRight",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x03",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadDown",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue" : "0x04",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadDownLeft",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue" : "0x05",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadLeft",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x06",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "dpadUpLeft",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x07",
|
||||
"mask": "0xF"
|
||||
},
|
||||
{
|
||||
"name": "share",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x10",
|
||||
"joystickNumber": 8
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x20",
|
||||
"joystickNumber": 9
|
||||
},
|
||||
{
|
||||
"name": "leftStick",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x40",
|
||||
"joystickNumber": 10
|
||||
},
|
||||
{
|
||||
"name": "rightStick",
|
||||
"buttonBlock": 6,
|
||||
"buttonValue": "0x80",
|
||||
"joystickNumber": 11
|
||||
}
|
||||
],
|
||||
"motionInputs" : [],
|
||||
"status" : [],
|
||||
"output": {
|
||||
"defaultBuffer":[
|
||||
5,255,4,0,0,0,0,0,0,0,0
|
||||
],
|
||||
"indexes": {
|
||||
"rumbleLeft": 4,
|
||||
"rumbleRight": 5,
|
||||
"red": 6,
|
||||
"green": 7,
|
||||
"blue": 8,
|
||||
"flashOn": 9,
|
||||
"flashOff": 10
|
||||
}
|
||||
},
|
||||
"touchPad": [{
|
||||
"name":"x1",
|
||||
"activePin": 35,
|
||||
"dataPinA": 37,
|
||||
"dataPinB": 36,
|
||||
"dataPinC": 38
|
||||
},{
|
||||
"name": "x2",
|
||||
"activePin": 39,
|
||||
"dataPinA": 41,
|
||||
"dataPinB": 40,
|
||||
"dataPinC": 42
|
||||
}]
|
||||
}
|
||||
8
dist/dualshock-controller/examples/README.md
vendored
Normal file
8
dist/dualshock-controller/examples/README.md
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
Examples:
|
||||
=========================
|
||||
|
||||
## Don't use this code as a starting point.
|
||||
I do things here that are not neccesary, you are better off looking at the readme in root as it has a cleaner implementation, this is here just so you can test it right away.
|
||||
|
||||
If you'd like to see more examples for the DualShock 4, feel free to check out
|
||||
[ds-examples](https://github.com/itaisteinherz/ds-examples).
|
||||
61
dist/dualshock-controller/examples/consolePrintControllerEvents.js
vendored
Normal file
61
dist/dualshock-controller/examples/consolePrintControllerEvents.js
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
var printcontrollerEvents = function(controller, controllerConfiguration) {
|
||||
'use strict';
|
||||
controller.on('left:move', function(data) {
|
||||
console.log('left Moved');
|
||||
console.log(data);
|
||||
});
|
||||
controller.on('right:move', function(data) {
|
||||
console.log('right Moved');
|
||||
console.log(data);
|
||||
});
|
||||
controller.on('connected', function() {
|
||||
console.log('connected');
|
||||
});
|
||||
|
||||
controller.on('error', function(data) {
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
var pressed = function(data) {
|
||||
console.log(data + ": press");
|
||||
};
|
||||
var released = function(data) {
|
||||
console.log(data + ": release");
|
||||
};
|
||||
var analog = function(data) {
|
||||
console.log(data + ": analog");
|
||||
};
|
||||
var hold = function(data) {
|
||||
console.log(data + ": hold");
|
||||
};
|
||||
var motion = function(motionInput, data) {
|
||||
console.log(motionInput);
|
||||
console.log(data);
|
||||
};
|
||||
//subscribe to all the buttons:
|
||||
for (var i = 0; i < controllerConfiguration.buttons.length; i++) {
|
||||
controller.on(controllerConfiguration.buttons[i].name + ":press", pressed);
|
||||
controller.on(controllerConfiguration.buttons[i].name + ":release", released);
|
||||
controller.on(controllerConfiguration.buttons[i].name + ":analog", analog);
|
||||
controller.on(controllerConfiguration.buttons[i].name + ":hold", hold);
|
||||
|
||||
}
|
||||
//subscribe to all the status events:
|
||||
if (controllerConfiguration.status && controllerConfiguration.status.length) {
|
||||
for (i = 0; i < controllerConfiguration.status.length; i++) {
|
||||
controller.on(controllerConfiguration.status[i].name + ":change", console.log);
|
||||
}
|
||||
}
|
||||
//subscribe to the motion events.
|
||||
controller.on('rightLeft' + ':motion', function(data) {
|
||||
motion('rightLeft', data);
|
||||
});
|
||||
controller.on('forwardBackward' + ':motion', function(data) {
|
||||
motion('forwardBackward', data);
|
||||
});
|
||||
controller.on('upDown' + ':motion', function(data) {
|
||||
motion('upDown', data);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = printcontrollerEvents;
|
||||
52
dist/dualshock-controller/examples/consolePrintDualShock3.js
vendored
Normal file
52
dist/dualshock-controller/examples/consolePrintDualShock3.js
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
'use strict';
|
||||
var dualShock = require('./../src/dualshock.js');
|
||||
var dualShock3;
|
||||
|
||||
//init the controller
|
||||
dualShock3 = dualShock({
|
||||
analogStickSmoothing: false,
|
||||
config: "dualShock3",
|
||||
logging: true
|
||||
});
|
||||
|
||||
// //for a client implementation we do not need this, this is only to test the inputs.
|
||||
var controllerConfiguration = require('./../controllerConfigurations/dualShock3');
|
||||
|
||||
// //init the print events
|
||||
var consolePrintEvents = require('./consolePrintControllerEvents')(dualShock3, controllerConfiguration);
|
||||
|
||||
dualShock3.on("dpadup:press", () => {
|
||||
dualShock3.setExtras({
|
||||
led: 2
|
||||
});
|
||||
});
|
||||
|
||||
dualShock3.on("dpadright:press", () => {
|
||||
dualShock3.setExtras({
|
||||
led: 4
|
||||
});
|
||||
});
|
||||
|
||||
dualShock3.on("dpaddown:press", () => {
|
||||
dualShock3.setExtras({
|
||||
led: 8
|
||||
});
|
||||
});
|
||||
|
||||
dualShock3.on("dpadleft:press", () => {
|
||||
dualShock3.setExtras({
|
||||
led: 16
|
||||
});
|
||||
});
|
||||
|
||||
dualShock3.on("r2:analog", (d) => {
|
||||
dualShock3.setExtras({
|
||||
rumbleRight: d
|
||||
});
|
||||
});
|
||||
|
||||
dualShock3.on("l2:press", (d) => {
|
||||
dualShock3.setExtras({
|
||||
rumbleLeft: 1
|
||||
});
|
||||
});
|
||||
59
dist/dualshock-controller/examples/consolePrintDualShock4.js
vendored
Normal file
59
dist/dualshock-controller/examples/consolePrintDualShock4.js
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
'use strict';
|
||||
var dualShock = require('./../src/dualshock.js');
|
||||
|
||||
var dualShock4 = dualShock({
|
||||
config: "dualShock4",
|
||||
logging: true
|
||||
});
|
||||
|
||||
//for a client implementation we do not need this, this is only to test the inputs.
|
||||
var controllerConfiguration = require('./../controllerConfigurations/dualShock4');
|
||||
|
||||
//init the print events
|
||||
var consolePrintEvents = require('./consolePrintControllerEvents')(dualShock4, controllerConfiguration);
|
||||
|
||||
dualShock4.on("dpadup:press", () => {
|
||||
dualShock4.setExtras({
|
||||
red: 255
|
||||
});
|
||||
});
|
||||
|
||||
dualShock4.on("dpadright:press", () => {
|
||||
dualShock4.setExtras({
|
||||
green: 255
|
||||
});
|
||||
});
|
||||
|
||||
dualShock4.on("dpaddown:press", () => {
|
||||
dualShock4.setExtras({
|
||||
blue: 255
|
||||
});
|
||||
});
|
||||
|
||||
dualShock4.on("dpadleft:press", () => {
|
||||
dualShock4.setExtras({
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
});
|
||||
});
|
||||
|
||||
dualShock4.on("x:press", (d) => {
|
||||
dualShock4.setExtras({
|
||||
red: 255,
|
||||
flashOn: 50,
|
||||
flashOff: 10
|
||||
});
|
||||
});
|
||||
|
||||
dualShock4.on("r2:analog", (d) => {
|
||||
dualShock4.setExtras({
|
||||
rumbleRight: d
|
||||
});
|
||||
});
|
||||
|
||||
dualShock4.on("l2:analog", (d) => {
|
||||
dualShock4.setExtras({
|
||||
rumbleLeft: d
|
||||
});
|
||||
});
|
||||
12
dist/dualshock-controller/examples/deviceDiscoveryHelp.js
vendored
Normal file
12
dist/dualshock-controller/examples/deviceDiscoveryHelp.js
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
var HID = require('node-hid');
|
||||
console.log(HID.devices());
|
||||
|
||||
var controller = new HID.HID(1356, 616);
|
||||
|
||||
controller.on('data', function(data) {
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
if (i === 30) {
|
||||
console.log(i + " " + data[i]);
|
||||
}
|
||||
}
|
||||
});
|
||||
6
dist/dualshock-controller/examples/deviceDiscoveryHelpLinux.js
vendored
Normal file
6
dist/dualshock-controller/examples/deviceDiscoveryHelpLinux.js
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
var joystick = require('joystick'),
|
||||
controller = new joystick(0, 256, 500);
|
||||
|
||||
controller.on('button', console.log);
|
||||
|
||||
controller.on('axis', console.log);
|
||||
67
dist/dualshock-controller/gruntfile.js
vendored
Normal file
67
dist/dualshock-controller/gruntfile.js
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
const files = ['gruntfile.js',
|
||||
'src/*.js',
|
||||
'src/inputProcessors/*.js',
|
||||
'test/*.js',
|
||||
'examples/*.js'
|
||||
];
|
||||
module.exports = function(grunt) {
|
||||
grunt.initConfig({
|
||||
watch: {
|
||||
files: files,
|
||||
tasks: ['default']
|
||||
},
|
||||
jshint: {
|
||||
files: files,
|
||||
options: {
|
||||
reporterOutput: "",
|
||||
esnext: true,
|
||||
node: true,
|
||||
globals: {
|
||||
describe: true,
|
||||
it: true,
|
||||
beforeEach: true
|
||||
}
|
||||
}
|
||||
},
|
||||
mochaTest: {
|
||||
test: {
|
||||
options: {
|
||||
reporter: 'spec'
|
||||
},
|
||||
src: ['test/*.js']
|
||||
}
|
||||
},
|
||||
jsbeautifier: {
|
||||
files: files,
|
||||
options: {
|
||||
js: {
|
||||
braceStyle: "collapse",
|
||||
breakChainedMethods: false,
|
||||
e4x: false,
|
||||
evalCode: false,
|
||||
indentChar: " ",
|
||||
indentLevel: 0,
|
||||
indentSize: 4,
|
||||
indentWithTabs: false,
|
||||
jslintHappy: false,
|
||||
keepArrayIndentation: false,
|
||||
keepFunctionIndentation: false,
|
||||
maxPreserveNewlines: 10,
|
||||
preserveNewlines: true,
|
||||
spaceBeforeConditional: true,
|
||||
spaceInParen: false,
|
||||
unescapeStrings: false,
|
||||
wrapLineLength: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
grunt.loadNpmTasks('grunt-mocha-test');
|
||||
grunt.loadNpmTasks('grunt-jsbeautifier');
|
||||
|
||||
grunt.registerTask('test', ['jshint', 'mochaTest']);
|
||||
grunt.registerTask('default', ['jshint', 'mochaTest', 'jsbeautifier']);
|
||||
};
|
||||
47
dist/dualshock-controller/package.json
vendored
Normal file
47
dist/dualshock-controller/package.json
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"name": "dualshock-controller",
|
||||
"version": "1.1.1",
|
||||
"description": "Eventing API layer over HID for the Sony DualShock 3 and DualShock 4 controllers ",
|
||||
"main": "./src/dualshock.js",
|
||||
"dependencies": {
|
||||
"node-hid": "^0.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "^1.0.1",
|
||||
"grunt-cli": "^1.2.0",
|
||||
"grunt-contrib-jshint": "^1.1.0",
|
||||
"grunt-contrib-watch": "^1.0.0",
|
||||
"grunt-mocha-test": "^0.13.2",
|
||||
"mocha": "^3.2.0",
|
||||
"sinon": "^1.8",
|
||||
"nyc": "^10.0.0",
|
||||
"grunt-jsbeautifier": "^0.2.7",
|
||||
"jshint": "^2.5.0",
|
||||
"mockery": "^2.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "grunt test",
|
||||
"coverage": "nyc npm test"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.2"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/rdepena/node-dualshock-controller.git"
|
||||
},
|
||||
"author": "ricardo de pena",
|
||||
"license": "MIT",
|
||||
"readmeFilename": "README.md",
|
||||
"keywords": [
|
||||
"ps3",
|
||||
"controller",
|
||||
"gamepad",
|
||||
"dualshock",
|
||||
"dualshock3",
|
||||
"dualshock4",
|
||||
"dual shock 3",
|
||||
"dual shock 4",
|
||||
"node-hid"
|
||||
]
|
||||
}
|
||||
BIN
dist/dualshock-controller/src/HID.node
vendored
Normal file
BIN
dist/dualshock-controller/src/HID.node
vendored
Normal file
Binary file not shown.
51
dist/dualshock-controller/src/analogs.js
vendored
Normal file
51
dist/dualshock-controller/src/analogs.js
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
'use strict';
|
||||
// Module dependencies.
|
||||
var dsutilities = require('./utilities'),
|
||||
Smoothing = require('./smoothing'),
|
||||
config = require('./config');
|
||||
|
||||
//Proccess Analog stick events.
|
||||
var Analogs = function(controller) {
|
||||
|
||||
var varianceThreshhold = 1,
|
||||
smoothInput = config.getOptions().analogStickSmoothing,
|
||||
outputSmoothing = new Smoothing(smoothInput),
|
||||
analogSticks = config.getControllerConfig().analogSticks;
|
||||
|
||||
//Private methods
|
||||
var processStick = function(analogStick, data) {
|
||||
var currentValue = {
|
||||
x: data[analogStick.x],
|
||||
y: data[analogStick.y]
|
||||
},
|
||||
previousValue = {
|
||||
x: outputSmoothing.readLastPosition(analogStick.name + 'x'),
|
||||
y: outputSmoothing.readLastPosition(analogStick.name + 'y')
|
||||
};
|
||||
|
||||
//we only raise an event if both
|
||||
if (dsutilities.isWithinVariance(previousValue.x, currentValue.x, varianceThreshhold) ||
|
||||
dsutilities.isWithinVariance(previousValue.y, currentValue.y, varianceThreshhold)) {
|
||||
|
||||
currentValue.x = outputSmoothing.smooth(analogStick.name + 'x', currentValue.x);
|
||||
currentValue.y = outputSmoothing.smooth(analogStick.name + 'y', currentValue.y);
|
||||
|
||||
// Update and emit
|
||||
if (controller[analogStick.name]) {
|
||||
controller[analogStick.name].x = currentValue.x;
|
||||
controller[analogStick.name].y = currentValue.y;
|
||||
}
|
||||
controller.emit(analogStick.name + ':move', currentValue);
|
||||
}
|
||||
};
|
||||
|
||||
// Public methods
|
||||
//process all the analog events.
|
||||
this.process = function(data) {
|
||||
for (var i = 0; i < analogSticks.length; i++) {
|
||||
processStick(analogSticks[i], data);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = Analogs;
|
||||
104
dist/dualshock-controller/src/buttons.js
vendored
Normal file
104
dist/dualshock-controller/src/buttons.js
vendored
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
'use strict';
|
||||
// Module dependencies.
|
||||
var config = require('./config'),
|
||||
dsutilities = require('./utilities');
|
||||
|
||||
//Proccess button events.
|
||||
var Buttons = function(controller) {
|
||||
|
||||
var buttons = config.getControllerConfig().buttons;
|
||||
|
||||
// convert strings to numbers, e.g. "0x01" to 0x01
|
||||
// must be converted because JSON doesn't allow numbers with leading zeros
|
||||
buttons.forEach(function(button) {
|
||||
if (typeof button.buttonValue == "string") {
|
||||
button.buttonValue = parseInt(button.buttonValue);
|
||||
}
|
||||
|
||||
if (typeof button.mask == "string") {
|
||||
button.mask = parseInt(button.mask);
|
||||
} else if (!(button.mask instanceof Number)) {
|
||||
button.mask = 0xFF;
|
||||
}
|
||||
|
||||
//generate event name aliases:
|
||||
button.eventPrefixes = dsutilities.generateEventPrefixAliases(button.name);
|
||||
});
|
||||
|
||||
|
||||
var buffer = {};
|
||||
|
||||
//Private methods
|
||||
var emitEvent = function(button, eventText, data) {
|
||||
button.eventPrefixes.forEach(function(eventPrefix) {
|
||||
controller.emit(eventPrefix + eventText, data);
|
||||
});
|
||||
};
|
||||
var processButton = function(button, data) {
|
||||
//make sure the data contains a value for the specified block
|
||||
//and bitwise operation for the button value
|
||||
|
||||
var block = data[button.buttonBlock] & button.mask;
|
||||
var hit = (block & button.buttonValue) == button.buttonValue;
|
||||
var value = 0;
|
||||
var state = 0; // 0: up, 1: down, 2: hold
|
||||
|
||||
// special case for the dualshock 4's dpadUp button as it causes the
|
||||
// lower 8 bits of it's block to be zeroed
|
||||
if (!button.buttonValue) {
|
||||
hit = !block;
|
||||
}
|
||||
|
||||
// special case for dualshock 4's dpad - they are not bitmasked values as
|
||||
// they cannot be pressed together - ie. up, left and upleft are three
|
||||
// different values - upleft is not equal to up & left
|
||||
if (button.buttonBlock == 5 && block < 0x08) {
|
||||
hit = block == button.buttonValue;
|
||||
}
|
||||
|
||||
if (hit) {
|
||||
value = 1;
|
||||
|
||||
//if the button is in the released state.
|
||||
if (!buffer[button.name]) {
|
||||
state = 1;
|
||||
buffer[button.name] = true;
|
||||
emitEvent(button, ':press', button.name);
|
||||
} else {
|
||||
state = 2;
|
||||
emitEvent(button, ':hold', button.name);
|
||||
}
|
||||
|
||||
//send the analog data
|
||||
if (button.analogPin && data[button.analogPin]) {
|
||||
emitEvent(button, ':analog', data[button.analogPin]);
|
||||
}
|
||||
|
||||
} else if (buffer[button.name]) {
|
||||
//button was pressed and is not released
|
||||
buffer[button.name] = false;
|
||||
|
||||
//button is no longer pressed, emit a analog 0 event.
|
||||
if (button.analogPin) {
|
||||
emitEvent(button, ':analog', 0);
|
||||
}
|
||||
//emit the released event.
|
||||
emitEvent(button, ':release', button.name);
|
||||
}
|
||||
|
||||
if (controller[button.name]) {
|
||||
controller[button.name].value = value;
|
||||
controller[button.name].state = state;
|
||||
}
|
||||
};
|
||||
|
||||
// Public methods
|
||||
//process all the analog events.
|
||||
this.process = function(data) {
|
||||
for (var i = 0; i < buttons.length; i++) {
|
||||
processButton(buttons[i], data);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = Buttons;
|
||||
56
dist/dualshock-controller/src/config.js
vendored
Normal file
56
dist/dualshock-controller/src/config.js
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
'use strict';
|
||||
// Module dependencies.
|
||||
// we will expose these objects via the manager.
|
||||
var options,
|
||||
controllerConfig;
|
||||
|
||||
//provides access to the current options and configs.
|
||||
var config = {
|
||||
setOptions: function(opts) {
|
||||
//no options were passed
|
||||
options = opts || {};
|
||||
|
||||
// Defaults:
|
||||
var defaultValues = {
|
||||
config: "dualShock3",
|
||||
accelerometerSmoothing: true,
|
||||
analogStickSmoothing: false,
|
||||
logging: false,
|
||||
forceNodeHid: false,
|
||||
linuxJoystickId: 0
|
||||
};
|
||||
|
||||
for (var name in defaultValues) {
|
||||
if (defaultValues.hasOwnProperty(name)) {
|
||||
var target = options[name];
|
||||
var orig = defaultValues[name];
|
||||
|
||||
if (!target) {
|
||||
options[name] = orig;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var controllerConfiguration;
|
||||
//use passed config or load from built-in configs
|
||||
if (typeof options.config === "object") {
|
||||
controllerConfiguration = options.config;
|
||||
} else {
|
||||
controllerConfiguration = require('./../controllerConfigurations/' + options.config);
|
||||
}
|
||||
|
||||
//set the current controllerConfiguration
|
||||
config.setControllerConfig(controllerConfiguration);
|
||||
},
|
||||
getOptions: function() {
|
||||
return options;
|
||||
},
|
||||
setControllerConfig: function(config) {
|
||||
controllerConfig = config;
|
||||
},
|
||||
getControllerConfig: function() {
|
||||
return controllerConfig;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
167
dist/dualshock-controller/src/controller.js
vendored
Normal file
167
dist/dualshock-controller/src/controller.js
vendored
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
// Module dependencies.
|
||||
const util = require('util'),
|
||||
dsutilities = require('./utilities'),
|
||||
Emitter = require('events').EventEmitter,
|
||||
Gyro = require('./gyro'),
|
||||
Analogs = require('./analogs'),
|
||||
Buttons = require('./buttons'),
|
||||
Status = require('./status'),
|
||||
HID = require('./HID'),
|
||||
config = require('./config'),
|
||||
TouchPad = require('./touchpad');
|
||||
|
||||
//generic controller object, it will need a controller Configuration with a buttons array passed into its connect function.
|
||||
const Controller = function() {
|
||||
'use strict';
|
||||
Emitter.call(this);
|
||||
|
||||
const controllerConfig = config.getControllerConfig(),
|
||||
options = config.getOptions(),
|
||||
indexes = controllerConfig.output.indexes,
|
||||
analogs = new Analogs(this),
|
||||
buttons = new Buttons(this),
|
||||
gyro = new Gyro(this),
|
||||
status = new Status(this),
|
||||
touchPad = new TouchPad(this);
|
||||
|
||||
let device = null;
|
||||
|
||||
[{
|
||||
type: 'analogSticks',
|
||||
properties: [{
|
||||
name: 'x',
|
||||
initialValue: 0
|
||||
}, {
|
||||
name: 'y',
|
||||
initialValue: 0
|
||||
}]
|
||||
}, {
|
||||
type: 'buttons',
|
||||
properties: [{
|
||||
name: 'state',
|
||||
initialValue: 0
|
||||
}, {
|
||||
name: 'value',
|
||||
initialValue: 0
|
||||
}]
|
||||
}, {
|
||||
type: 'motionInputs',
|
||||
properties: [{
|
||||
name: 'value',
|
||||
initialValue: 0
|
||||
}, {
|
||||
name: 'direction',
|
||||
initialValue: 0
|
||||
}]
|
||||
}, {
|
||||
type: 'status',
|
||||
properties: [{
|
||||
name: 'state',
|
||||
initialValue: ''
|
||||
}]
|
||||
}].forEach(function(setup) {
|
||||
const entities = controllerConfig[setup.type],
|
||||
properties = setup.properties;
|
||||
|
||||
if (entities.length) {
|
||||
entities.forEach(function(entity) {
|
||||
this[entity.name] = properties.reduce(function(accum, property) {
|
||||
return (accum[property.name] = property.initialValue, accum);
|
||||
}, {});
|
||||
}, this);
|
||||
}
|
||||
}, this);
|
||||
|
||||
//Private methods
|
||||
//emit an error event or log it to the console.
|
||||
const handleException = function(ex) {
|
||||
//if exception was generated within our stream
|
||||
if (this && this.emit) {
|
||||
this.emit('error', ex);
|
||||
} else {
|
||||
dsutilities.warn(ex);
|
||||
throw (ex);
|
||||
}
|
||||
};
|
||||
|
||||
//process data from HID connected device.
|
||||
const processFrame = function(data) {
|
||||
if (controllerConfig.motionInputs) {
|
||||
gyro.process(data);
|
||||
}
|
||||
if (controllerConfig.analogSticks) {
|
||||
analogs.process(data);
|
||||
}
|
||||
if (controllerConfig.buttons) {
|
||||
buttons.process(data);
|
||||
}
|
||||
if (controllerConfig.status) {
|
||||
status.process(data);
|
||||
}
|
||||
if (controllerConfig.touchPad) {
|
||||
touchPad.process(data);
|
||||
}
|
||||
};
|
||||
|
||||
const isController = function(device) {
|
||||
var vendor = (device.vendorId == controllerConfig.vendorId);
|
||||
var product = (device.productId == controllerConfig.productId || device.productId == 2508);
|
||||
return vendor && product;
|
||||
};
|
||||
|
||||
// Public methods
|
||||
this.connect = function() {
|
||||
dsutilities.warn('connect method is deprecated, controller now connects upon declaration.');
|
||||
};
|
||||
|
||||
this.disconnect = function() {
|
||||
if (device && device.close) {
|
||||
device.close();
|
||||
}
|
||||
this.emit('disconnecting');
|
||||
dsutilities.warn('node dualshock disconnecting');
|
||||
};
|
||||
|
||||
// Used to set controller rumble and light
|
||||
this.setExtras = function(data) {
|
||||
|
||||
let buff = controllerConfig.output.defaultBuffer.slice();
|
||||
|
||||
Object.keys(data).forEach(k => {
|
||||
buff[indexes[k]] = data[k];
|
||||
});
|
||||
device.write(buff);
|
||||
};
|
||||
|
||||
//connect to the controller.
|
||||
if (typeof options.device === 'undefined') {
|
||||
dsutilities.warn('node dualshock connecting');
|
||||
var devices = HID.devices();
|
||||
|
||||
const deviceMeta = devices
|
||||
.filter(isController)[0];
|
||||
if (deviceMeta) {
|
||||
device = new HID.HID(deviceMeta.path);
|
||||
console.log(device);
|
||||
device.on('data', processFrame.bind(this));
|
||||
device.on('error', handleException.bind(this));
|
||||
} else {
|
||||
handleException(new Error(`device with Vendor ID:${controllerConfig.vendorId} Product ID:${controllerConfig.productId} not found`));
|
||||
}
|
||||
|
||||
} else {
|
||||
// Allow user-specified device
|
||||
device = options.device;
|
||||
console.log("Custom device: ", device);
|
||||
device.on('data', processFrame.bind(this));
|
||||
device.on('error', handleException.bind(this));
|
||||
}
|
||||
|
||||
//subscribe to the exit event:
|
||||
process.on('exit', this.disconnect.bind(this));
|
||||
};
|
||||
|
||||
//need to inherit from event emiter.
|
||||
util.inherits(Controller, Emitter);
|
||||
|
||||
module.exports = Controller;
|
||||
22
dist/dualshock-controller/src/dualshock.js
vendored
Normal file
22
dist/dualshock-controller/src/dualshock.js
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
// Module dependencies.
|
||||
var Controller = require('./controller'),
|
||||
config = require('./config');
|
||||
|
||||
// This is the app entry point.
|
||||
// options you can pass:
|
||||
// {
|
||||
// config : "File from controllerConfigurations" or a JS object containing configuration,
|
||||
// accelerometerSmoothing : true/false, this will activate motion/acelerometer output smoothing. true by default.
|
||||
// analogStickSmoothing : true/false, this will activate analog thumb stick smoothing
|
||||
// }
|
||||
var dualShock = function(options) {
|
||||
'use strict';
|
||||
|
||||
//set the current options
|
||||
config.setOptions(options);
|
||||
|
||||
//returns the controller.
|
||||
return new Controller();
|
||||
};
|
||||
|
||||
module.exports = dualShock;
|
||||
72
dist/dualshock-controller/src/gyro.js
vendored
Normal file
72
dist/dualshock-controller/src/gyro.js
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
'use strict';
|
||||
// Module dependencies.
|
||||
var dsutilities = require('./utilities'),
|
||||
Smoothing = require('./smoothing'),
|
||||
config = require('./config');
|
||||
|
||||
//Proccess button events.
|
||||
var motionProcessor = function(controller) {
|
||||
|
||||
var varianceThreshhold = 1,
|
||||
smoothInput = config.getOptions().accelerometerSmoothing,
|
||||
outputSmoothing = new Smoothing(smoothInput),
|
||||
motionInputs = config.getControllerConfig().motionInputs;
|
||||
|
||||
//generate event name aliases:
|
||||
motionInputs.forEach(function(motionAxis) {
|
||||
motionAxis.eventPrefixes = dsutilities.generateEventPrefixAliases(motionAxis.name);
|
||||
});
|
||||
|
||||
//Private methods
|
||||
//data corrections so that each dirrection has a 0 throug x value
|
||||
var correctData = function(motionAxis, data) {
|
||||
var value;
|
||||
//ensuring that both directions start from 0 and move to -x or x;
|
||||
if (data[motionAxis.directionPin] === 1) {
|
||||
//we need the values to be from 0 to x.
|
||||
value = 255 - data[motionAxis.valuePin];
|
||||
} else if (data[motionAxis.directionPin] === 2) {
|
||||
//going in the oposite direction, we need to values to be from 0 to -x;
|
||||
value = data[motionAxis.valuePin] * -1;
|
||||
}
|
||||
|
||||
//return an object with both value and dirrection.
|
||||
return {
|
||||
direction: data[motionAxis.directionPin],
|
||||
value: value
|
||||
};
|
||||
};
|
||||
|
||||
//process the axis movement.
|
||||
var processAxis = function(motionAxis, data) {
|
||||
//every motion will have a dirrection and a value
|
||||
var motionValue = correctData(motionAxis, data),
|
||||
lastPosition = outputSmoothing.readLastPosition(motionAxis.name);
|
||||
|
||||
//check if the values are within variance
|
||||
if (dsutilities.isWithinVariance(lastPosition, motionValue.value, varianceThreshhold)) {
|
||||
motionValue.value = outputSmoothing.smooth(motionAxis.name, motionValue.value);
|
||||
|
||||
// Don't assign motionValue directly to controller[motionAxis.name],
|
||||
// this will break the reference.
|
||||
if (controller[motionAxis.name]) {
|
||||
controller[motionAxis.name].value = motionValue.value;
|
||||
controller[motionAxis.name].direction = motionValue.direction;
|
||||
}
|
||||
|
||||
motionAxis.eventPrefixes.forEach(function(eventPrefix) {
|
||||
controller.emit(eventPrefix + ':motion', motionValue);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Public methods
|
||||
//process all configured motion inputs.
|
||||
this.process = function(data) {
|
||||
for (var i = 0; i < motionInputs.length; i++) {
|
||||
processAxis(motionInputs[i], data);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = motionProcessor;
|
||||
50
dist/dualshock-controller/src/smoothing.js
vendored
Normal file
50
dist/dualshock-controller/src/smoothing.js
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// Module dependencies.
|
||||
|
||||
//smooths data with moving average
|
||||
var outputSmoothing = function(smoothInput) {
|
||||
'use strict';
|
||||
|
||||
var buffer = {},
|
||||
maxFrames = 5;
|
||||
|
||||
// Public methods
|
||||
this.readLastPosition = function(motionAxisName) {
|
||||
var axisBuffer = buffer[motionAxisName];
|
||||
return axisBuffer ? axisBuffer[axisBuffer.length - 1] : null;
|
||||
};
|
||||
|
||||
this.addToBuffer = function(motionAxisName, value) {
|
||||
if (buffer[motionAxisName]) {
|
||||
//add the current value to the buffer
|
||||
buffer[motionAxisName].push(value);
|
||||
|
||||
//remove the head of the buffer
|
||||
if (buffer[motionAxisName].length > maxFrames) {
|
||||
buffer[motionAxisName].shift();
|
||||
}
|
||||
} else {
|
||||
//create an array with the value.
|
||||
buffer[motionAxisName] = [value];
|
||||
}
|
||||
};
|
||||
|
||||
//smooth using a moving average.
|
||||
this.smooth = function(motionAxisName, value) {
|
||||
this.addToBuffer(motionAxisName, value);
|
||||
var axisBuffer = buffer[motionAxisName],
|
||||
sum = 0,
|
||||
smoothedVal = value;
|
||||
|
||||
if (smoothInput) {
|
||||
for (var i = 0; i < axisBuffer.length; i++) {
|
||||
sum += axisBuffer[i];
|
||||
}
|
||||
smoothedVal = Math.floor(sum / axisBuffer.length);
|
||||
}
|
||||
|
||||
return smoothedVal;
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = outputSmoothing;
|
||||
36
dist/dualshock-controller/src/status.js
vendored
Normal file
36
dist/dualshock-controller/src/status.js
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
'use strict';
|
||||
// Module dependencies.
|
||||
|
||||
var config = require('./config');
|
||||
|
||||
//Proccess button events.
|
||||
var Status = function(controller) {
|
||||
|
||||
var buffer = {},
|
||||
status = config.getControllerConfig().status;
|
||||
|
||||
var processControllerStatus = function(category, data) {
|
||||
var state;
|
||||
for (var i = 0; i < category.states.length; i++) {
|
||||
if (data[category.pin] === category.states[i].value) {
|
||||
state = category.states[i].state;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer[category.name] !== state) {
|
||||
if (controller[category.name]) {
|
||||
controller[category.name].state = state;
|
||||
}
|
||||
controller.emit(category.name + ':change', state);
|
||||
}
|
||||
buffer[category.name] = state;
|
||||
};
|
||||
|
||||
this.process = function(data) {
|
||||
for (var i = 0; i < status.length; i++) {
|
||||
processControllerStatus(status[i], data);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = Status;
|
||||
52
dist/dualshock-controller/src/touchpad.js
vendored
Normal file
52
dist/dualshock-controller/src/touchpad.js
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
const config = require('./config');
|
||||
|
||||
function genpBufferFromConf(tpAxis) {
|
||||
return {
|
||||
name: tpAxis.name,
|
||||
active: false,
|
||||
data: {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = function TouchPad(controller) {
|
||||
const touchPad = config.getControllerConfig().touchPad;
|
||||
let pBuffer = {};
|
||||
|
||||
function processIsActive(buffer, tpAxis) {
|
||||
const active = buffer[tpAxis.activePin] < 128;
|
||||
const axisBuffer = pBuffer[tpAxis.name];
|
||||
const evt = active ? 'active' : 'inactive';
|
||||
|
||||
if (active !== axisBuffer.active) {
|
||||
controller.emit(`touchpad:${tpAxis.name}:${evt}`);
|
||||
}
|
||||
|
||||
axisBuffer.active = active;
|
||||
}
|
||||
|
||||
function processData(buffer, tpAxis) {
|
||||
const axisBuffer = pBuffer[tpAxis.name];
|
||||
|
||||
if (axisBuffer.active) {
|
||||
axisBuffer.data.x = ((buffer[tpAxis.dataPinA] & 15) << 8 | buffer[tpAxis.dataPinB]);
|
||||
axisBuffer.data.y = buffer[tpAxis.dataPinC] << 4 | ((buffer[tpAxis.dataPinA] & 240) >> 4);
|
||||
controller.emit(`touchpad:${tpAxis.name}`, axisBuffer.data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.process = function process(buffer) {
|
||||
for (let i = 0; i < touchPad.length; i++) {
|
||||
//if we have not built a pBuffer profile for this axis lets build it.
|
||||
if (!pBuffer[touchPad[i].name]) {
|
||||
pBuffer[touchPad[i].name] = genpBufferFromConf(touchPad[i]);
|
||||
}
|
||||
|
||||
processIsActive(buffer, touchPad[i]);
|
||||
processData(buffer, touchPad[i]);
|
||||
}
|
||||
};
|
||||
};
|
||||
34
dist/dualshock-controller/src/utilities.js
vendored
Normal file
34
dist/dualshock-controller/src/utilities.js
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
'use strict';
|
||||
// Module dependencies.
|
||||
|
||||
var config = require('./config');
|
||||
|
||||
var unique = function unique(x) {
|
||||
var result = [];
|
||||
for (var i = 0; i < x.length; i++) {
|
||||
if ((result.indexOf(x[i]) < 0)) {
|
||||
result.push(x[i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
//provide a few utility functions.
|
||||
module.exports = {
|
||||
|
||||
//reduces noise from the controller
|
||||
isWithinVariance: function(x, y, varianceThreshhold) {
|
||||
return Math.abs(x - y) > varianceThreshhold;
|
||||
},
|
||||
warn: function(message) {
|
||||
if (config.getOptions().logging) {
|
||||
console.log(message);
|
||||
}
|
||||
},
|
||||
generateEventPrefixAliases: function(eventPrefix) {
|
||||
return unique([
|
||||
eventPrefix,
|
||||
eventPrefix.toLowerCase()
|
||||
]);
|
||||
}
|
||||
};
|
||||
84
dist/dualshock-controller/test/analog.tests.js
vendored
Normal file
84
dist/dualshock-controller/test/analog.tests.js
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
// Module dependencies.
|
||||
var Analogs = require('../src/analogs'),
|
||||
assert = require('assert'),
|
||||
sinon = require('sinon'),
|
||||
EventEmitter = require('events').EventEmitter,
|
||||
config = require('../src/config');
|
||||
|
||||
describe('the Analogs component', function() {
|
||||
'use strict';
|
||||
|
||||
var mockConfig = [{
|
||||
"name": "analog",
|
||||
"x": 0,
|
||||
"y": 1
|
||||
}],
|
||||
instance = [{
|
||||
name: 'process'
|
||||
}],
|
||||
dataA = [50, 65],
|
||||
dataB = [0, 0],
|
||||
analogs,
|
||||
emitter,
|
||||
spy;
|
||||
|
||||
beforeEach(function() {
|
||||
emitter = new EventEmitter();
|
||||
config.setOptions({
|
||||
analogStickSmoothing: false
|
||||
});
|
||||
config.setControllerConfig({
|
||||
analogSticks: mockConfig
|
||||
});
|
||||
analogs = new Analogs(emitter);
|
||||
spy = new sinon.spy();
|
||||
});
|
||||
|
||||
describe('object instance', function() {
|
||||
it('should have the following shape', function() {
|
||||
//make sure we find these functions.
|
||||
instance.forEach(function(method) {
|
||||
assert.equal(typeof analogs[method.name], 'function');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('move events', function() {
|
||||
it('should invoke the move event', function() {
|
||||
emitter.on('analog:move', spy);
|
||||
analogs.process(dataA);
|
||||
|
||||
assert.equal(spy.called, true);
|
||||
});
|
||||
it('should not invoke the move event', function() {
|
||||
emitter.on('analog:move', spy);
|
||||
analogs.process(dataB);
|
||||
|
||||
assert.equal(spy.called, false);
|
||||
});
|
||||
it('should invoke the move event with zero', function() {
|
||||
analogs.process(dataA);
|
||||
emitter.on('analog:move', spy);
|
||||
analogs.process(dataB);
|
||||
|
||||
assert.equal(spy.called, true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('return values', function() {
|
||||
it('should return the analog values', function() {
|
||||
emitter.on('analog:move', spy);
|
||||
analogs.process(dataA);
|
||||
var expectedValue = {
|
||||
x: 50,
|
||||
y: 65
|
||||
},
|
||||
spyArgument = spy.args[0][0];
|
||||
|
||||
assert.equal(expectedValue.x, spyArgument.x);
|
||||
assert.equal(expectedValue.y, spyArgument.y);
|
||||
assert.equal(spy.called, true);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
178
dist/dualshock-controller/test/buttons.tests.js
vendored
Normal file
178
dist/dualshock-controller/test/buttons.tests.js
vendored
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
// Module dependencies.
|
||||
var Buttons = require('../src/buttons'),
|
||||
assert = require('assert'),
|
||||
sinon = require('sinon'),
|
||||
EventEmitter = require('events').EventEmitter,
|
||||
config = require('../src/config');
|
||||
|
||||
describe('the Buttons component', function() {
|
||||
'use strict';
|
||||
|
||||
var mockConfig = [{
|
||||
"name": "buttonName",
|
||||
"buttonBlock": 0,
|
||||
"buttonValue": "0x08",
|
||||
"analogPin": 1
|
||||
}, {
|
||||
"name": "dpadUp",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x00",
|
||||
"mask": "0xF"
|
||||
}, {
|
||||
"name": "dpadDown",
|
||||
"buttonBlock": 5,
|
||||
"buttonValue": "0x01",
|
||||
"mask": "0xF"
|
||||
}],
|
||||
instance = [{
|
||||
name: 'process'
|
||||
}],
|
||||
dataA = [8, 170],
|
||||
dataB = [0, 0],
|
||||
buttons,
|
||||
emitter,
|
||||
spy,
|
||||
spyLowerCaseEvents;
|
||||
|
||||
beforeEach(function() {
|
||||
emitter = new EventEmitter();
|
||||
config.setControllerConfig({
|
||||
buttons: mockConfig
|
||||
});
|
||||
buttons = new Buttons(emitter);
|
||||
spy = new sinon.spy();
|
||||
spyLowerCaseEvents = new sinon.spy();
|
||||
|
||||
});
|
||||
|
||||
describe('object instance', function() {
|
||||
it('should have the following shape', function() {
|
||||
//make sure we find these functions.
|
||||
instance.forEach(function(method) {
|
||||
assert.equal(typeof buttons[method.name], 'function');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('press events', function() {
|
||||
it('should envoke the buttonName:press', function() {
|
||||
emitter.on('buttonName:press', spy);
|
||||
emitter.on('buttonname:press', spyLowerCaseEvents);
|
||||
buttons.process(dataA);
|
||||
|
||||
assert.equal(spy.called, true);
|
||||
assert.equal(spyLowerCaseEvents.called, true);
|
||||
});
|
||||
it('should not envoke the buttonName:press', function() {
|
||||
emitter.on('buttonName:release', spy);
|
||||
emitter.on('buttonname:release', spyLowerCaseEvents);
|
||||
buttons.process(dataB);
|
||||
|
||||
assert.equal(spy.called, false);
|
||||
assert.equal(spyLowerCaseEvents.called, false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('release events', function() {
|
||||
it('should envoke the buttonName:release', function() {
|
||||
emitter.on('buttonName:release', spy);
|
||||
emitter.on('buttonname:release', spyLowerCaseEvents);
|
||||
buttons.process(dataA);
|
||||
buttons.process(dataB);
|
||||
|
||||
assert.equal(spy.called, true);
|
||||
assert.equal(spyLowerCaseEvents.called, true);
|
||||
});
|
||||
it('should not envoke the buttonName:release', function() {
|
||||
emitter.on('buttonName:release', spy);
|
||||
emitter.on('buttonname:release', spyLowerCaseEvents);
|
||||
buttons.process(dataA);
|
||||
|
||||
assert.equal(spy.called, false);
|
||||
assert.equal(spyLowerCaseEvents.called, false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('button hold', function() {
|
||||
it('should raise the hold event', function() {
|
||||
emitter.on('buttonName:hold', spy);
|
||||
emitter.on('buttonname:hold', spyLowerCaseEvents);
|
||||
buttons.process(dataA);
|
||||
buttons.process(dataA);
|
||||
assert.equal(spy.args[0][0], 'buttonName');
|
||||
assert.equal(spy.called, true);
|
||||
assert.equal(spyLowerCaseEvents.called, true);
|
||||
});
|
||||
|
||||
it('should not raise the hold event', function() {
|
||||
emitter.on('buttonName:hold', spy);
|
||||
emitter.on('buttonname:hold', spyLowerCaseEvents);
|
||||
buttons.process(dataB);
|
||||
buttons.process(dataB);
|
||||
assert.equal(spy.called, false);
|
||||
assert.equal(spyLowerCaseEvents.called, false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('button analog', function() {
|
||||
it('should raise the analog event', function() {
|
||||
emitter.on('buttonName:analog', spy);
|
||||
emitter.on('buttonname:analog', spyLowerCaseEvents);
|
||||
buttons.process(dataA);
|
||||
|
||||
assert.equal(spy.args[0][0], dataA[1]);
|
||||
assert.equal(spy.called, true);
|
||||
assert.equal(spyLowerCaseEvents.called, true);
|
||||
});
|
||||
|
||||
it('should not raise the analog event', function() {
|
||||
emitter.on('buttonName:analog', spy);
|
||||
emitter.on('buttonname:analog', spyLowerCaseEvents);
|
||||
buttons.process(dataB);
|
||||
|
||||
assert.equal(spy.called, false);
|
||||
assert.equal(spyLowerCaseEvents.called, false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ps4 dpad up button', function() {
|
||||
it('should emit the dpadUp:press event', function() {
|
||||
emitter.on('dpadUp:press', spy);
|
||||
emitter.on('dpadup:press', spyLowerCaseEvents);
|
||||
buttons.process([0, 0, 0, 0, 0, 0]);
|
||||
|
||||
assert.equal(spy.called, true);
|
||||
assert.equal(spyLowerCaseEvents.called, true);
|
||||
});
|
||||
|
||||
it('should not emit the dpadDown:press event', function() {
|
||||
emitter.on('dpadDown:press', spy);
|
||||
emitter.on('dpaddown:press', spyLowerCaseEvents);
|
||||
buttons.process([0, 0, 0, 0, 0, 0]);
|
||||
|
||||
assert.equal(spy.called, false);
|
||||
assert.equal(spyLowerCaseEvents.called, false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ps4 dpad down button', function() {
|
||||
it('should emit the dpadDown:press event', function() {
|
||||
emitter.on('dpadDown:press', spy);
|
||||
emitter.on('dpaddown:press', spyLowerCaseEvents);
|
||||
buttons.process([0, 0, 0, 0, 0, parseInt("00001001", 2)]);
|
||||
|
||||
assert.equal(spy.called, true);
|
||||
assert.equal(spyLowerCaseEvents.called, true);
|
||||
});
|
||||
|
||||
it('should not emit the dpadUp:press event', function() {
|
||||
emitter.on('dpadUp:press', spy);
|
||||
emitter.on('dpadup:press', spyLowerCaseEvents);
|
||||
buttons.process([0, 0, 0, 0, 0, parseInt("00001001", 2)]);
|
||||
|
||||
assert.equal(spy.called, false);
|
||||
assert.equal(spyLowerCaseEvents.called, false);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
91
dist/dualshock-controller/test/config.tests.js
vendored
Normal file
91
dist/dualshock-controller/test/config.tests.js
vendored
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
|
||||
describe('The Config component', function() {
|
||||
var mockConfig = {
|
||||
vendorId: 1556,
|
||||
productId: 616,
|
||||
output: []
|
||||
},
|
||||
mockOptions = {
|
||||
config: 'dualShock3',
|
||||
accelerometerSmoothing: true,
|
||||
logging: false
|
||||
},
|
||||
instance = [{
|
||||
name: 'setOptions'
|
||||
}, {
|
||||
name: 'getOptions'
|
||||
}, {
|
||||
name: 'setControllerConfig'
|
||||
}, {
|
||||
name: 'getControllerConfig'
|
||||
}],
|
||||
defaultOptionsInstance = [{
|
||||
name: 'config'
|
||||
}, {
|
||||
name: 'accelerometerSmoothing'
|
||||
}, {
|
||||
name: 'analogStickSmoothing'
|
||||
}],
|
||||
configA,
|
||||
configB;
|
||||
|
||||
beforeEach(function() {
|
||||
configA = require('../src/config');
|
||||
configB = require('../src/config');
|
||||
});
|
||||
|
||||
|
||||
describe('object instance', function() {
|
||||
it('should have the following shape', function() {
|
||||
instance.forEach(function(method) {
|
||||
assert.equal(typeof configA[method.name], 'function');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('option methods', function() {
|
||||
it('should be able to save options', function() {
|
||||
configA.setOptions(mockOptions);
|
||||
assert.equal(configA.getOptions(), mockOptions);
|
||||
});
|
||||
|
||||
it('should provide a single object accross instances', function() {
|
||||
configA.setOptions(mockOptions);
|
||||
assert.equal(configA.getOptions(), configB.getOptions());
|
||||
});
|
||||
});
|
||||
|
||||
describe('controllerConfig methods', function() {
|
||||
it('should be able to save controllerConfig settings', function() {
|
||||
configA.setControllerConfig(mockConfig);
|
||||
//change the object
|
||||
mockConfig.vendorId = 22;
|
||||
assert.equal(configA.getControllerConfig(), mockConfig);
|
||||
});
|
||||
|
||||
it('should provide a single object accross instances', function() {
|
||||
configA.setControllerConfig(mockConfig);
|
||||
assert.equal(configA.getControllerConfig(), configB.getControllerConfig());
|
||||
});
|
||||
});
|
||||
|
||||
describe('default values', function() {
|
||||
beforeEach(function() {
|
||||
configA.setOptions();
|
||||
});
|
||||
it('should apply default values', function() {
|
||||
var ops = configA.getOptions();
|
||||
defaultOptionsInstance.forEach(function(property) {
|
||||
assert.notEqual(ops[property.name], void 0);
|
||||
});
|
||||
});
|
||||
it('should load default config', function() {
|
||||
var controllerConfig = configA.getControllerConfig();
|
||||
assert.notEqual(controllerConfig, null);
|
||||
assert.notEqual(controllerConfig, void 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
158
dist/dualshock-controller/test/dualshock.tests.js
vendored
Normal file
158
dist/dualshock-controller/test/dualshock.tests.js
vendored
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
var assert = require('assert'),
|
||||
Emitter = require('events').EventEmitter,
|
||||
config = require('../src/config'),
|
||||
mockery = require('mockery');
|
||||
|
||||
function Device() {
|
||||
Emitter.call(this);
|
||||
}
|
||||
|
||||
Device.prototype = Object.create(Emitter.prototype, {
|
||||
constructor: {
|
||||
value: Device
|
||||
},
|
||||
close: {
|
||||
value: function() {}
|
||||
}
|
||||
});
|
||||
|
||||
var config = {
|
||||
analogSticks: [{
|
||||
name: 'foo',
|
||||
x: 0,
|
||||
y: 1
|
||||
}],
|
||||
buttons: [{
|
||||
name: 'bar',
|
||||
buttonBlock: 0,
|
||||
buttonValue: 0x08,
|
||||
analogPin: 1
|
||||
}],
|
||||
motionInputs: [{
|
||||
name: 'baz',
|
||||
directionPin: 0,
|
||||
valuePin: 1
|
||||
}],
|
||||
status: [{
|
||||
name: 'quz',
|
||||
pin: 5,
|
||||
states: [{
|
||||
value: 0,
|
||||
state: 'Charging'
|
||||
}, {
|
||||
value: 2,
|
||||
state: 'Charging'
|
||||
}, {
|
||||
value: 3,
|
||||
state: '40%'
|
||||
}]
|
||||
}],
|
||||
output: []
|
||||
};
|
||||
|
||||
var analogs = config.analogSticks;
|
||||
var buttons = config.buttons;
|
||||
var motions = config.motionInputs;
|
||||
var status = config.status;
|
||||
|
||||
describe('the DualShock component', function() {
|
||||
//enable mockery and mock node-hid:
|
||||
mockery.enable();
|
||||
var nodeHidMock = {
|
||||
HID: function(vendor, productId) {
|
||||
return {
|
||||
on: function() {
|
||||
//could use this at some point. nothing atm.
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
//register mock node-hid.
|
||||
mockery.registerMock('node-hid', nodeHidMock);
|
||||
|
||||
//once mockery is up we can require the dualshock module.
|
||||
var DualShock = require('./../src/dualshock.js'),
|
||||
controller, device;
|
||||
|
||||
before(function() {
|
||||
device = new Device();
|
||||
controller = DualShock({
|
||||
config: config,
|
||||
device: device
|
||||
});
|
||||
});
|
||||
|
||||
//disable mockery so it does not interfere with other tests.
|
||||
after(function() {
|
||||
mockery.deregisterMock('node-hid');
|
||||
mockery.disable();
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
controller.removeAllListeners(Object.keys(controller._events));
|
||||
});
|
||||
|
||||
describe('analog properties', function() {
|
||||
analogs.forEach(function(analog) {
|
||||
it(analog.name, function() {
|
||||
assert.equal(controller[analog.name].x, 0);
|
||||
assert.equal(controller[analog.name].y, 0);
|
||||
|
||||
device.emit('data', [100, 100]);
|
||||
|
||||
assert.equal(controller[analog.name].x, 100);
|
||||
assert.equal(controller[analog.name].y, 100);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('button properties', function() {
|
||||
buttons.forEach(function(button) {
|
||||
it(button.name, function() {
|
||||
assert.equal(controller[button.name].value, 0);
|
||||
assert.equal(controller[button.name].state, 0);
|
||||
|
||||
device.emit('data', [8, 170]);
|
||||
|
||||
assert.equal(controller[button.name].value, 1);
|
||||
assert.equal(controller[button.name].state, 1);
|
||||
|
||||
device.emit('data', [8, 170]);
|
||||
|
||||
assert.equal(controller[button.name].value, 1);
|
||||
assert.equal(controller[button.name].state, 2);
|
||||
|
||||
device.emit('data', [0, 0]);
|
||||
|
||||
assert.equal(controller[button.name].value, 0);
|
||||
assert.equal(controller[button.name].state, 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('motion properties', function() {
|
||||
motions.forEach(function(motion) {
|
||||
it(motion.name, function() {
|
||||
assert.equal(controller[motion.name].value, 0);
|
||||
assert.equal(controller[motion.name].direction, 0);
|
||||
|
||||
device.emit('data', [1, 233]);
|
||||
|
||||
assert.equal(controller[motion.name].value, 22);
|
||||
assert.equal(controller[motion.name].direction, 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('status properties', function() {
|
||||
status.forEach(function(stat) {
|
||||
it(stat.name, function() {
|
||||
device.emit('data', [0, 0, 0, 0, 1, 3]);
|
||||
assert.equal(controller[stat.name].state, '40%');
|
||||
|
||||
device.emit('data', [0, 0, 0, 0, 1, 2]);
|
||||
assert.equal(controller[stat.name].state, 'Charging');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
78
dist/dualshock-controller/test/gyro.tests.js
vendored
Normal file
78
dist/dualshock-controller/test/gyro.tests.js
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
// Module dependencies.
|
||||
var Gyro = require('../src/gyro'),
|
||||
assert = require('assert'),
|
||||
sinon = require('sinon'),
|
||||
EventEmitter = require('events').EventEmitter,
|
||||
config = require('../src/config');
|
||||
|
||||
describe('the Gyro component', function() {
|
||||
'use strict';
|
||||
|
||||
var mockConfig = [{
|
||||
name: "dirrection",
|
||||
directionPin: 0,
|
||||
valuePin: 1
|
||||
}],
|
||||
instance = [{
|
||||
name: 'process'
|
||||
}],
|
||||
dataA = [1, 130],
|
||||
dataB = [2, 130],
|
||||
emitter,
|
||||
spy,
|
||||
gyro;
|
||||
|
||||
beforeEach(function() {
|
||||
emitter = new EventEmitter();
|
||||
spy = sinon.spy();
|
||||
config.setOptions({
|
||||
accelerometerSmoothing: false
|
||||
});
|
||||
config.setControllerConfig({
|
||||
motionInputs: mockConfig
|
||||
});
|
||||
gyro = new Gyro(emitter);
|
||||
});
|
||||
|
||||
describe('object instance', function() {
|
||||
|
||||
it('should have the following shape', function() {
|
||||
//make sure we find these functions.
|
||||
instance.forEach(function(method) {
|
||||
assert.equal(typeof gyro[method.name], 'function');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('process()', function() {
|
||||
|
||||
it('should envoke the dirrection:motion event with positive values', function() {
|
||||
emitter.on('dirrection:motion', spy);
|
||||
gyro.process(dataA);
|
||||
assert.equal(spy.called, true);
|
||||
var expectedValue = {
|
||||
direction: 1,
|
||||
value: 125
|
||||
},
|
||||
spyArgument = spy.args[0][0];
|
||||
|
||||
assert.equal(spyArgument.direction, expectedValue.direction);
|
||||
assert.equal(spyArgument.value, expectedValue.value);
|
||||
});
|
||||
|
||||
it('should envoke the dirrection event with negative values.', function() {
|
||||
emitter.on('dirrection:motion', spy);
|
||||
gyro.process(dataB);
|
||||
assert.equal(spy.called, true);
|
||||
var expectedValue = {
|
||||
direction: 2,
|
||||
value: -130
|
||||
},
|
||||
spyArgument = spy.args[0][0];
|
||||
|
||||
assert.equal(spyArgument.direction, expectedValue.direction);
|
||||
assert.equal(spyArgument.value, expectedValue.value);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
71
dist/dualshock-controller/test/smoothing.tests.js
vendored
Normal file
71
dist/dualshock-controller/test/smoothing.tests.js
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// Module dependencies.
|
||||
var Smoothing = require('../src/smoothing'),
|
||||
assert = require('assert');
|
||||
|
||||
describe('the smoothing component', function() {
|
||||
'use strict';
|
||||
|
||||
var smoothing,
|
||||
nonSmoothing,
|
||||
//an instance should have the following functions.
|
||||
instance = [{
|
||||
name: 'readLastPosition'
|
||||
}, {
|
||||
name: 'addToBuffer'
|
||||
}, {
|
||||
name: 'smooth'
|
||||
}];
|
||||
|
||||
beforeEach(function() {
|
||||
smoothing = new Smoothing(true);
|
||||
nonSmoothing = new Smoothing(false);
|
||||
for (var i = 0; i < 5; i++) {
|
||||
nonSmoothing.addToBuffer('testNonSmoothing', i);
|
||||
smoothing.addToBuffer('one', i);
|
||||
smoothing.addToBuffer('two', i + 1);
|
||||
smoothing.addToBuffer('testSmoothing', i);
|
||||
}
|
||||
});
|
||||
|
||||
describe('object instance', function() {
|
||||
it('should have the following shape', function() {
|
||||
//make sure we find these functions.
|
||||
instance.forEach(function(method) {
|
||||
assert.equal(typeof smoothing[method.name], 'function');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('addToBuffer()', function() {
|
||||
it('should add values to the buffer', function() {
|
||||
smoothing.addToBuffer('one', 6);
|
||||
smoothing.addToBuffer('two', 7);
|
||||
|
||||
assert.equal(smoothing.readLastPosition('one'), 6);
|
||||
assert.equal(smoothing.readLastPosition('two'), 7);
|
||||
});
|
||||
});
|
||||
|
||||
describe('readLastPosition()', function() {
|
||||
it('should handle buffers for different objects', function() {
|
||||
assert.equal(smoothing.readLastPosition('one'), 4);
|
||||
assert.equal(smoothing.readLastPosition('two'), 5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('smooth()', function() {
|
||||
it('should return expected values when smoothing', function() {
|
||||
//with the data set smoothing of 6 should be 3.
|
||||
assert.equal(smoothing.smooth('testSmoothing', 6), 3);
|
||||
//with the data set smoothing of 8 should be 4.
|
||||
assert.equal(smoothing.smooth('testSmoothing', 8), 4);
|
||||
});
|
||||
|
||||
it('should return expected values when not smoothing', function() {
|
||||
//with smoothing turned off 9 should return 9
|
||||
assert.equal(nonSmoothing.smooth('testNonSmoothing', 9), 9);
|
||||
//with smoothing turned off 6 should return 6
|
||||
assert.equal(nonSmoothing.smooth('testNonSmoothing', 6), 6);
|
||||
});
|
||||
});
|
||||
});
|
||||
60
dist/dualshock-controller/test/status.tests.js
vendored
Normal file
60
dist/dualshock-controller/test/status.tests.js
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// Module dependencies.
|
||||
var Status = require('../src/status'),
|
||||
assert = require('assert'),
|
||||
sinon = require('sinon'),
|
||||
EventEmitter = require('events').EventEmitter,
|
||||
config = require('../src/config');
|
||||
|
||||
describe('the status component', function() {
|
||||
var mockConfig = [{
|
||||
"name": "chargingState",
|
||||
"pin": 0,
|
||||
"states": [{
|
||||
"value": 0,
|
||||
"state": "Charging"
|
||||
}, {
|
||||
"value": 2,
|
||||
"state": "Charging"
|
||||
}, {
|
||||
"value": 3,
|
||||
"state": "40%"
|
||||
}]
|
||||
}],
|
||||
dataA = [0, 0],
|
||||
dataB = [0, 3],
|
||||
instance = [{
|
||||
name: 'process'
|
||||
}],
|
||||
status,
|
||||
emitter,
|
||||
spy;
|
||||
|
||||
beforeEach(function() {
|
||||
emitter = new EventEmitter();
|
||||
spy = sinon.spy();
|
||||
config.setControllerConfig({
|
||||
status: mockConfig
|
||||
});
|
||||
status = new Status(emitter, mockConfig);
|
||||
});
|
||||
|
||||
describe('object instance', function() {
|
||||
it('should have the following shape', function() {
|
||||
//make sure we find these functions.
|
||||
instance.forEach(function(method) {
|
||||
assert.equal(typeof status[method.name], 'function');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('process()', function() {
|
||||
it('process should return an object with the expected values', function() {
|
||||
emitter.on('chargingState:change', spy);
|
||||
status.process(dataA);
|
||||
var spyArgument = spy.args[0][0];
|
||||
assert.equal(typeof spyArgument, 'string');
|
||||
assert.equal(spyArgument, 'Charging');
|
||||
spyArgument = null;
|
||||
});
|
||||
});
|
||||
});
|
||||
BIN
dist/fonts/Gilroy-ExtraBold.otf
vendored
Normal file
BIN
dist/fonts/Gilroy-ExtraBold.otf
vendored
Normal file
Binary file not shown.
BIN
dist/fonts/Gilroy-Light.otf
vendored
Normal file
BIN
dist/fonts/Gilroy-Light.otf
vendored
Normal file
Binary file not shown.
BIN
dist/icons/plus.png
vendored
Normal file
BIN
dist/icons/plus.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.3 KiB |
0
html/jquery.min.js → html/dep/jquery.min.js
vendored
0
html/jquery.min.js → html/dep/jquery.min.js
vendored
|
|
@ -1,141 +0,0 @@
|
|||
mainMenuItems = ['games', 'settings', 'account', 'store', 'files'];
|
||||
gameMenuItems = ['slimey', 'test2'];
|
||||
settingMenuItems = ['fullscreen', 'auto-update'];
|
||||
settingMenuItemValues = [0,1];
|
||||
selected = 1;
|
||||
var menuType = 'horizontal';
|
||||
var looper = 1;
|
||||
function updateSettings(){
|
||||
looper = 1;
|
||||
settingMenuItems.forEach(function(name){
|
||||
$("#option-" + settingMenuItems[looper - 1] + " label input").prop( "checked", settingMenuItemValues[looper - 1]);
|
||||
looper++;
|
||||
})
|
||||
looper = 1;
|
||||
}
|
||||
setInterval(updateSettings, 400);
|
||||
function select(){
|
||||
if(start == true){
|
||||
showMainMenu();
|
||||
return;
|
||||
}
|
||||
if(window.menu == 1){
|
||||
if(selected == 1){
|
||||
window.menu = 2;
|
||||
window.selected = 1;
|
||||
$(".button-main.selected").removeClass("selected");
|
||||
$(".button-main-container.selected").removeClass("selected");
|
||||
menuType = 'horizontal';
|
||||
selectGameMenuItem();
|
||||
} else if(selected == 2){
|
||||
window.selected = 1
|
||||
window.menu = 3;
|
||||
$(".button-main.selected").removeClass("selected");
|
||||
$(".button-main-container.selected").removeClass("selected");
|
||||
menuType = 'vertical';
|
||||
selectSettingMenuItem();
|
||||
}
|
||||
} else if(window.menu == 3) {
|
||||
$("#option-" + settingMenuItems[selected - 1] + " label input").prop( "checked", !$("#option-" + settingMenuItems[selected - 1] + " label input").prop( "checked") );
|
||||
settingMenuItemValues[selected - 1] = $("#option-" + settingMenuItems[selected - 1] + " label input").prop( "checked");
|
||||
if(settingMenuItems[selected - 1] == 'fullscreen'){
|
||||
sendFullscreen(settingMenuItemValues[selected - 1]);
|
||||
}
|
||||
} else if(window.menu == 2){
|
||||
if(selected == 1){
|
||||
$("#game-slimey .game-preview").css('transition', '0.5s width, 0.5s height, 0.5s max-height, 0.3s top, 0.3s left, 0.5s opacity');
|
||||
$("#game-slimey .game-preview").css('-webkit-transition', '0.5s width, 0.5s height, 0.5s max-height, 0.3s top, 0.3s left, 0.5s opacity');
|
||||
$("#game-slimey .game-preview").css('-moz-transition', '0.5s width, 0.5s height, 0.5s max-height, 0.3s top, 0.3s left, 0.5s opacity');
|
||||
$("#game-slimey .game-preview").width($(document).width());
|
||||
$("#game-slimey .game-preview").height("auto");
|
||||
var gamePosition = $("#game-slimey .game-preview").position();
|
||||
$("#game-slimey .game-preview").css("left", gamePosition.left);
|
||||
$("#game-slimey .game-preview").css("top", gamePosition.top);
|
||||
$("#game-slimey .game-preview").css("position", "fixed");
|
||||
$("#game-slimey .game-preview").css("z-index", "990");
|
||||
$("#game-slimey .game-preview").css("left", 0);
|
||||
$("#game-slimey .game-preview").css("top", 0);
|
||||
$("#game-slimey .game-preview").css("opacity", 0);
|
||||
setTimeout(() => {
|
||||
$("#game-slimey .game-preview").attr("style", '');
|
||||
$("#game-slimey .game-preview").hide();
|
||||
startGame(0);
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
function deselect(){
|
||||
if(menu == 2 || menu == 3){
|
||||
$(".selected").removeClass("selected");
|
||||
switch(menu){
|
||||
case 2:
|
||||
window.selected = 1;
|
||||
break;
|
||||
case 3:
|
||||
window.selected = 2;
|
||||
break;
|
||||
}
|
||||
window.menu = 1;
|
||||
menuType = 'horizontal';
|
||||
selectMainMenuItem();
|
||||
} else if(menu == 1){
|
||||
showStartMenu();
|
||||
}
|
||||
}
|
||||
function callMenuItem(){
|
||||
switch(menu){
|
||||
case 1:
|
||||
selectMainMenuItem();
|
||||
break;
|
||||
case 2:
|
||||
selectGameMenuItem();
|
||||
break;
|
||||
case 3:
|
||||
selectSettingMenuItem();
|
||||
break;
|
||||
}
|
||||
}
|
||||
function selectSettingMenuItem(){
|
||||
if(selected > settingMenuItems.length){
|
||||
selected = 1;
|
||||
} else if(selected < 1){
|
||||
selected = settingMenuItems.length;
|
||||
}
|
||||
console.log("Selected item no. " + selected);
|
||||
$(".option.selected").removeClass("selected");
|
||||
$("#option-" + settingMenuItems[selected - 1]).addClass("selected");
|
||||
}
|
||||
function selectGameMenuItem(){
|
||||
if(selected > gameMenuItems.length){
|
||||
selected = 1;
|
||||
} else if(selected < 1){
|
||||
selected = gameMenuItems.length;
|
||||
}
|
||||
console.log("Selected item no. " + selected);
|
||||
$(".game.selected").removeClass("selected");
|
||||
$("#game-" + gameMenuItems[selected - 1]).addClass("selected");
|
||||
|
||||
$(".game").width((function(offset, width){
|
||||
return $(".game .game-preview:eq(" + offset + ")").width();
|
||||
}));
|
||||
}
|
||||
function selectMainMenuItem(){
|
||||
if(selected > mainMenuItems.length){
|
||||
selected = 1;
|
||||
} else if(selected < 1){
|
||||
selected = mainMenuItems.length;
|
||||
}
|
||||
$(".menu-item").addClass("hidden");
|
||||
console.log("Selected item no. " + selected);
|
||||
$(".button-main.selected").removeClass("selected");
|
||||
$(".button-main-container.selected").removeClass("selected");
|
||||
$("#" + mainMenuItems[selected - 1]).addClass("selected");
|
||||
$("#" + mainMenuItems[selected - 1] + "-view").removeClass("hidden");
|
||||
$(".button-main-container:has(.selected)").addClass("selected");
|
||||
|
||||
if(selected == 1){
|
||||
$(".game").width((function(offset, width){
|
||||
return $(".game .game-preview:eq(" + offset + ")").width();
|
||||
}));
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
297
html/gamepad/input.js
Normal file
297
html/gamepad/input.js
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
/**
|
||||
* (C) Daniel Bulant
|
||||
* This file IS NOT subject to the LICENSE.
|
||||
* Permission is hereby granted by Daniel Bulant to be used exclusively in console-hub (unless otherwise stated).
|
||||
* Want to use this script? Contact me at admin@danbulant.eu
|
||||
*/
|
||||
|
||||
class EventEmitter {
|
||||
constructor() {
|
||||
var delegate = document.createDocumentFragment();
|
||||
[
|
||||
'addEventListener',
|
||||
'dispatchEvent',
|
||||
'removeEventListener'
|
||||
].forEach(f =>
|
||||
this[f] = (...xs) => delegate[f](...xs)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class Controls extends EventEmitter {
|
||||
players = [];
|
||||
binds = {};
|
||||
gamepads = 0;
|
||||
gamepadArray = {};
|
||||
axisMinimum = 0.7;
|
||||
constructor() {
|
||||
super();
|
||||
this.isChrome = !!window.chrome && (!!window.chrome.webstore || !!window.chrome.runtime);
|
||||
|
||||
//Gamepad support
|
||||
if (!this.hasGamepads()) {
|
||||
console.log("Gamepads not supported!");
|
||||
} else {
|
||||
this.initGamepads();
|
||||
}
|
||||
|
||||
var player = this.addPlayer();
|
||||
console.log("(DEFAULT) Player joined using Keyboard");
|
||||
|
||||
player.controlledBy = {
|
||||
type: "keyboard",
|
||||
id: 1
|
||||
}
|
||||
|
||||
this.defaultBindings(player, "KEY1");
|
||||
|
||||
var interval = setInterval(this.update, 1000 / 60, this, this.binds);//update the properties at 60FPS
|
||||
|
||||
this.listenForKeys();
|
||||
}
|
||||
|
||||
listenForKeys() {
|
||||
this.keys = {};
|
||||
window.onkeyup = e => { this.keys[e.key] = false; if (e.key == "Tab") { e.preventDefault() } }
|
||||
window.onkeydown = e => { this.keys[e.key] = true; if (e.key == "Tab") { e.preventDefault() } }
|
||||
}
|
||||
|
||||
update(controls, bindings) {
|
||||
controls.players.forEach((player) => {
|
||||
var gamepad = {};
|
||||
if (player.controlledBy.type == "controller") {
|
||||
gamepad = navigator.getGamepads()[player.controlledBy.id];
|
||||
}
|
||||
var binds = bindings[player.id];
|
||||
for (var bind in binds) {
|
||||
if (!binds.hasOwnProperty(bind)) continue;
|
||||
var isPressed = controls.checkPressed(binds[bind], gamepad);
|
||||
if (isPressed && isPressed != player[bind + "Pressed"]) {
|
||||
var event = new Event(player.id + "-" + bind);
|
||||
event.player = player;
|
||||
event.bindings = binds;
|
||||
controls.dispatchEvent(event);
|
||||
}
|
||||
if (isPressed) {
|
||||
var event = new Event("while_" + player.id + "-" + bind);
|
||||
event.player = player;
|
||||
event.bindings = binds;
|
||||
controls.dispatchEvent(event);
|
||||
}
|
||||
player[bind + "Pressed"] = isPressed;
|
||||
}
|
||||
});
|
||||
}
|
||||
while(player, event, callback) {
|
||||
return this.addEventListener("while_" + player + "-" + event, callback);
|
||||
}
|
||||
on(player, event, callback) {
|
||||
return this.addEventListener(player + "-" + event, callback);
|
||||
}
|
||||
once(player, event, callback) {
|
||||
var controls = this;
|
||||
return this.addEventListener(player + "-" + event, function cb(e) {
|
||||
controls.removeEventListener(e.type, cb);
|
||||
callback(e);
|
||||
});
|
||||
}
|
||||
checkPressed(str, gamepad) {
|
||||
var bind = this.parseBind(str);
|
||||
if (bind.type == "button") {
|
||||
return gamepad.buttons[bind.value].pressed;
|
||||
}
|
||||
if (bind.type == "key") {
|
||||
return this.keys[bind.value] == true;
|
||||
}
|
||||
if (bind.type == "unknown") {
|
||||
return null;
|
||||
}
|
||||
if (bind.type == "axis") {
|
||||
var value = gamepad.axes[bind.value];
|
||||
if (bind.axis == "+") {
|
||||
return value > this.axisMinimum;
|
||||
} else if (bind.axis == "-") {
|
||||
return value < -this.axisMinimum;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
parseBind(str) {
|
||||
var bind = {};
|
||||
if (str.substr(0, 1) == "b") {
|
||||
bind.type = "button";
|
||||
} else if (str.substr(0, 1) == "a") {
|
||||
bind.type = "axis";
|
||||
} else if (str.substr(0, 1) == "k") {
|
||||
bind.type = "key";
|
||||
str = str.substr(1);//remove the -
|
||||
} else {
|
||||
bind.type = "unknown";
|
||||
}
|
||||
|
||||
str = str.substr(1);
|
||||
if (bind.type == "axis") {
|
||||
bind.axis = str.substr(str.length - 1);
|
||||
str = str.substr(0, str.length - 1);
|
||||
} else {
|
||||
bind.axis = null;
|
||||
}
|
||||
bind.value = str;
|
||||
return bind;
|
||||
}
|
||||
initGamepads() {
|
||||
var controls = this;
|
||||
window.addEventListener("gamepadconnected", function (e) {
|
||||
console.log("Player joined using %s", e.gamepad.id);
|
||||
controls.gamepads++;
|
||||
controls.gamepadArray[e.gamepad.index] = e.gamepad;
|
||||
var player = controls.addPlayer();
|
||||
|
||||
|
||||
player.controlledBy = {
|
||||
type: "controller",
|
||||
id: e.gamepad.index
|
||||
}
|
||||
|
||||
controls.defaultBindings(player, "DS4");
|
||||
});
|
||||
|
||||
window.addEventListener("gamepaddisconnected", function (e) {
|
||||
console.log("Player %d disconnected", e.gamepad.index);
|
||||
controls.gamepads--;
|
||||
controls.gamepadArray[e.gamepad.index] = undefined;
|
||||
|
||||
controls.removeBindings()
|
||||
});
|
||||
}
|
||||
|
||||
defaultBindings(player, type) {
|
||||
var binding = {};
|
||||
switch (type) {
|
||||
case "DS4":
|
||||
binding = {
|
||||
home: "b16",
|
||||
action: "b0",
|
||||
jump: "b1",
|
||||
forward: "a1-",
|
||||
backward: "a1+",
|
||||
left: "a0-",
|
||||
right: "a0+",
|
||||
options: "b9",
|
||||
cancel: "b1"
|
||||
}
|
||||
break;
|
||||
case "KEY1":
|
||||
binding = {
|
||||
home: "k-Tab",
|
||||
action: "k-Enter",
|
||||
jump: "k- ",
|
||||
forward: "k-w",
|
||||
backward: "k-s",
|
||||
left: "k-a",
|
||||
right: "k-d",
|
||||
options: "k-Escape",
|
||||
cancel: "k-Backspace"
|
||||
}
|
||||
break;
|
||||
}
|
||||
this.binds[player.id] = binding
|
||||
}
|
||||
/**
|
||||
* Try vibrating
|
||||
* @param {*} player player which controller is to be vibrated
|
||||
* @param {*} length ms to vibrate
|
||||
* @param {*} weak percents to vibrate
|
||||
* @param {*} strong percents to vibrate
|
||||
* @returns {Boolean} if vibration was possible (is supported)
|
||||
*/
|
||||
vibrate(player, length = 200, weak = 100, strong = 100) {
|
||||
try {
|
||||
if (player.controlledBy.type == "controller") {
|
||||
navigator.getGamepads()[player.controlledBy.id].vibrationActuator.playEffect("dual-rumble", {
|
||||
startDelay: 0,
|
||||
duration: length,
|
||||
weakMagnitude: weak / 100,
|
||||
strongMagnitude: strong / 100
|
||||
});
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* DEBUG
|
||||
*/
|
||||
reportOnGamepad() {
|
||||
var gp = navigator.getGamepads()[0];
|
||||
var html = "";
|
||||
html += "id: " + gp.id + "<br/>";
|
||||
|
||||
for (var i = 0; i < gp.buttons.length; i++) {
|
||||
html += "Button " + (i + 1) + ": ";
|
||||
if (gp.buttons[i].pressed) html += " pressed";
|
||||
html += "<br/>";
|
||||
}
|
||||
|
||||
for (var i = 0; i < gp.axes.length; i += 2) {
|
||||
html += "Stick " + (Math.ceil(i / 2) + 1) + ": " + gp.axes[i] + ", " + gp.axes[i + 1] + "<br/>";
|
||||
}
|
||||
|
||||
document.getElementById("gamepadDisplay").innerHTML = html;
|
||||
}
|
||||
hasGamepads() {
|
||||
return "getGamepads" in navigator;
|
||||
}
|
||||
|
||||
playerCount() {
|
||||
return this.players.length;
|
||||
}
|
||||
addPlayer() {
|
||||
var player = this.players[this.players.length] = {};
|
||||
player.id = this.players.length;
|
||||
player.forwardPressed = false;
|
||||
player.backwardPressed = false;
|
||||
player.rightPressed = false;
|
||||
player.leftPressed = false;
|
||||
player.jumpPressed = false;
|
||||
player.actionPressed = false;
|
||||
player.homePressed = false;
|
||||
player.optionsPressed = false;
|
||||
player.controlledBy = null;
|
||||
|
||||
return player;
|
||||
}
|
||||
addBinding(player, key, action) {
|
||||
if (!this.binds[player]) this.binds[player] = {};
|
||||
if (!this.binds[player][key]) this.binds[player][key] = [];
|
||||
this.binds[player][key][this.binds[player][key].length] = action;
|
||||
|
||||
return this;//chaining
|
||||
}
|
||||
removeBinding(player, key, action = null) {
|
||||
if (action = null) {
|
||||
this.binds[player][key] = [];
|
||||
} else {
|
||||
this.removeItem(this.binds[player][key], action);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
removeBindings(player) {
|
||||
this.binds[player] = {};
|
||||
}
|
||||
removeItem(arr) {
|
||||
var what, a = arguments, L = a.length, ax;
|
||||
while (L > 1 && arr.length) {
|
||||
what = a[--L];
|
||||
while ((ax = arr.indexOf(what)) !== -1) {
|
||||
arr.splice(ax, 1);
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
79
html/gamepad/keyboard.js
Normal file
79
html/gamepad/keyboard.js
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
$(document).keypress((event) => {
|
||||
if(event.which == 39){//RIGHT
|
||||
if(menuType == 'horizontal'){
|
||||
goRight();
|
||||
}
|
||||
} else if(event.which == 40){//DOWN
|
||||
if(menuType != 'horizontal'){
|
||||
goRight();
|
||||
}
|
||||
} else if(event.which == 37){//LEFT
|
||||
if(menuType == 'horizontal'){
|
||||
goLeft();
|
||||
}
|
||||
} else if(event.which == 38){//UP
|
||||
if(menuType != 'horizontal'){
|
||||
goLeft();
|
||||
}
|
||||
} else if(event.which == 13){
|
||||
select();
|
||||
event.preventDefault();
|
||||
} else if(event.which == 27){
|
||||
deselect();
|
||||
event.preventDefault();
|
||||
}
|
||||
})
|
||||
var keyBinds = new Map();
|
||||
keyBinds.set('button_1', 'enter')//set button_1 (A) as enter
|
||||
var pressed = [];
|
||||
var gameLooper = 0;
|
||||
//Sending keys from gamepad
|
||||
function changeToGame(){
|
||||
//Called when game is running
|
||||
//reset events
|
||||
gamepad.off('press', 'start');
|
||||
gamepad.off('press', 'd_pad_left');
|
||||
gamepad.off('press', 'd_pad_right');
|
||||
gamepad.off('press', 'button_1');
|
||||
gamepad.off('press', 'button_2');
|
||||
//add custom ones
|
||||
keyBinds.forEach((val, key) => {
|
||||
gamepad.on('press', key, () => {
|
||||
sendKeys([], val);
|
||||
})
|
||||
})
|
||||
|
||||
// gameLooper = setInterval(() => {
|
||||
// if(pressed == []) return;
|
||||
// sendKeys(pressed);
|
||||
// pressed = [];
|
||||
// }, 100);
|
||||
}
|
||||
|
||||
function changeToMenu(){
|
||||
clearInterval(gameLooper); //stop sending keys
|
||||
//Called when game is stopped
|
||||
//reset events
|
||||
gamepad.off('press', 'start');
|
||||
gamepad.off('press', 'd_pad_left');
|
||||
gamepad.off('press', 'd_pad_right');
|
||||
gamepad.off('press', 'button_1');
|
||||
gamepad.off('press', 'button_2');
|
||||
//restore original events
|
||||
gamepad.on('press', 'start', () => {
|
||||
showMainMenu();
|
||||
});
|
||||
|
||||
gamepad.on('press', 'd_pad_left', () => {
|
||||
goLeft();
|
||||
});
|
||||
gamepad.on('press', 'd_pad_right', () => {
|
||||
goRight();
|
||||
});
|
||||
gamepad.on('press', 'button_1', () => {
|
||||
select();
|
||||
});
|
||||
gamepad.on('press', 'button_2', () => {
|
||||
deselect();
|
||||
});
|
||||
}
|
||||
40
html/gamepad/ps4controller.js
Normal file
40
html/gamepad/ps4controller.js
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
//Only to be used for PS4 (Dualshock 4) controllers
|
||||
var dualShock = require('../../dist/dualshock-controller/src/dualshock');
|
||||
try {
|
||||
|
||||
var controller = dualShock({
|
||||
config: "dualshock4-generic-driver"
|
||||
});
|
||||
|
||||
controller.on('error', err => console.log(err));
|
||||
|
||||
function setExtras(obj) {
|
||||
controller.setExtras(obj);
|
||||
}
|
||||
var connected = false;
|
||||
|
||||
controller.on('connected', () => {
|
||||
connected = true;
|
||||
});
|
||||
controller.on("disconnected", () => {
|
||||
connected = false;
|
||||
})
|
||||
|
||||
function isConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
function on(event, handler) {
|
||||
controller.on(event, handler);
|
||||
}
|
||||
module.exports = {
|
||||
on,
|
||||
isConnected,
|
||||
setExtras
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
module.exports = {
|
||||
isConnected: ()=>{return false}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
function startGame(int){
|
||||
if(int == 0){//SLIMEY JUMP
|
||||
var game = document.createElement('iframe');
|
||||
game.id = 'frame';
|
||||
game.src = "https://v6p9d9t4.ssl.hwcdn.net/html/1493204/index.html";
|
||||
game.style.position = 'fixed';
|
||||
game.style.width = '100%';
|
||||
game.style.zIndex = '990';
|
||||
}
|
||||
}
|
||||
185
html/index.html
185
html/index.html
|
|
@ -1,185 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Console hub (ALPHA)</title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"><!-- include material icons -->
|
||||
<link rel="stylesheet" href="styles.min.css"><!-- include custom stylesheet -->
|
||||
</head>
|
||||
<body>
|
||||
<div class="welcome-main">
|
||||
<h1 class="title-font">Welcome</h1>
|
||||
<h5 class="glow" id="welcome-continue">Press <b>START</b> to procceed</h5>
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<div class="profile">
|
||||
Not loged in
|
||||
</div>
|
||||
<div class="info">
|
||||
<i class="material-icons">
|
||||
network_wifi
|
||||
</i>
|
||||
<span class="time" id="time">
|
||||
00:00
|
||||
</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="selection" class="menu hidden">
|
||||
|
||||
<div id="games-view" class="menu-item hidden">
|
||||
<h1 class="title-font">Games</h1>
|
||||
|
||||
<div class="previews">
|
||||
<div class="game" id="game-slimey">
|
||||
<img src="https://console.danbulant.eu/games/slimey.png" class="game-preview">
|
||||
<span>Slimey, JUMP</span>
|
||||
</div>
|
||||
<div class="game" id="game-test2">
|
||||
<img src="https://cdn.supercell.com/supercell.com/190617073222/supercell.com/files/styles/hero_image_narrow/public/hero_bg_brawlstars_.jpg?itok=yifeFssz×tamp=1544532809" class="game-preview">
|
||||
<span>Brawl stars</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="settings-view" class="menu-item hidden">
|
||||
<h1 class="title-font">Settings</h1>
|
||||
|
||||
<div class="switch-label option switchable" id="option-fullscreen">
|
||||
<span>Fullscreen</span>
|
||||
<label class="switch">
|
||||
<input type="checkbox">
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="switch-label option switchable" id="option-auto-update">
|
||||
<span>Auto updates</span>
|
||||
<label class="switch">
|
||||
<input type="checkbox" checked>
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="account-view" class="menu-item hidden">
|
||||
<h1 class="title-font">Account</h1>
|
||||
<p>You aren't signed in</p>
|
||||
<button>Login/register</button>
|
||||
</div>
|
||||
|
||||
<div id="store-view" class="menu-item hidden">
|
||||
<h1 class="title-font">Store</h1>
|
||||
<p>Not available in ALHPA stage</p>
|
||||
</div>
|
||||
|
||||
<div id="files-view" class="menu-item hidden">
|
||||
<h1 class="title-font">Files</h1>
|
||||
<p class="path"></p>
|
||||
<div id="files-list-container">
|
||||
<ul id="files-list">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="dialog" class="hidden">
|
||||
<h1 id="dialog-title">TITLE</h1>
|
||||
<p id="dialog-text">TEXT</p>
|
||||
<div id="dialog-buttons">
|
||||
<button id="dialog-button_1">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="main-menu" class="main hidden">
|
||||
|
||||
<div class="button-main-container">
|
||||
<div class="button-main selected" id="games">
|
||||
<div class="button-menu" id="games-menu">
|
||||
<i class="material-icons">
|
||||
videogame_asset
|
||||
</i>
|
||||
</div>
|
||||
<span>
|
||||
Games
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="button-main-container">
|
||||
<div class="button-main " id="settings">
|
||||
<div class="button-menu" id="settings-menu">
|
||||
<i class="material-icons">
|
||||
settings
|
||||
</i>
|
||||
</div>
|
||||
<span>
|
||||
Settings
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="button-main-container">
|
||||
<div class="button-main " id="account">
|
||||
<div class="button-menu" id="account-menu">
|
||||
<i class="material-icons">
|
||||
account_box
|
||||
</i>
|
||||
</div>
|
||||
<span>
|
||||
Account
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="button-main-container">
|
||||
<div class="button-main " id="store">
|
||||
<div class="button-menu" id="store-menu">
|
||||
<i class="material-icons">
|
||||
local_grocery_store
|
||||
</i>
|
||||
</div>
|
||||
<span>
|
||||
Store
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="button-main-container">
|
||||
<div class="button-main " id="files">
|
||||
<div class="button-menu" id="files-menu">
|
||||
<i class="material-icons">
|
||||
folder_open
|
||||
</i>
|
||||
</div>
|
||||
<span>
|
||||
Files
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="back" class="back-arrow hidden">
|
||||
<i class="material-icons circle">
|
||||
arrow_back
|
||||
</i>
|
||||
<span id="back-text">back</span>
|
||||
</div>
|
||||
|
||||
<div id="snackbar"></div>
|
||||
<script>if (typeof module === 'object') {window.module = module; module = undefined;}</script>
|
||||
<script src="jquery.min.js"></script>
|
||||
<script>if (window.module) module = window.module;</script>
|
||||
<script src="gamepad.js"></script>
|
||||
<script src="functions.js"></script>
|
||||
<script src="script.js"></script>
|
||||
<script src="onlineChecker.js"></script>
|
||||
<script src="filesystem.js"></script>
|
||||
|
||||
<script src="node.js"></script>
|
||||
|
||||
<script src="keyboard.js"></script>
|
||||
<script>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
$(document).keypress((event) => {
|
||||
if(event.which == 39){//RIGHT
|
||||
if(menuType == 'horizontal'){
|
||||
goRight();
|
||||
}
|
||||
} else if(event.which == 40){//DOWN
|
||||
if(menuType != 'horizontal'){
|
||||
goRight();
|
||||
}
|
||||
} else if(event.which == 37){//LEFT
|
||||
if(menuType == 'horizontal'){
|
||||
goLeft();
|
||||
}
|
||||
} else if(event.which == 38){//UP
|
||||
if(menuType != 'horizontal'){
|
||||
goLeft();
|
||||
}
|
||||
} else if(event.which == 13){
|
||||
select();
|
||||
event.preventDefault();
|
||||
} else if(event.which == 27){
|
||||
deselect();
|
||||
event.preventDefault();
|
||||
}
|
||||
})
|
||||
63
html/node.js
63
html/node.js
|
|
@ -1,37 +1,30 @@
|
|||
if(typeof module !== undefined){
|
||||
var wifi, wifiQuality;
|
||||
// In renderer process (web page).
|
||||
const { ipcRenderer } = require('electron')
|
||||
// In renderer process (web page).
|
||||
const { ipcRenderer } = require('electron')
|
||||
|
||||
ipcRenderer.on('wifi', (event, arg) => {
|
||||
wifi = arg;
|
||||
wifi = wifi;
|
||||
})
|
||||
ipcRenderer.on('wifiQuality', (event, arg) => {
|
||||
wifiQuality = arg;
|
||||
wifiQuality = wifiQuality;
|
||||
})
|
||||
ipcRenderer.on('fullscreen', (event, arg) => {
|
||||
console.log("Fullscreen now " + arg);
|
||||
settingMenuItemValues[0] = arg;
|
||||
})
|
||||
function sendFullscreen(bool){
|
||||
console.log("Sending fullscreen " + bool);
|
||||
ipcRenderer.send('fullscreen', bool);
|
||||
}
|
||||
ipcRenderer.send('get-data', 'wifi');
|
||||
ipcRenderer.send('get-data', 'wifiQuality');
|
||||
var files = null;
|
||||
ipcRenderer.on('listFiles', (event, arg) => {
|
||||
console.log(arg);
|
||||
files = arg;
|
||||
refreshFiles();
|
||||
})
|
||||
var loc = window.location.pathname;
|
||||
var dir = loc.substring(1, loc.lastIndexOf('/'));//fix file:///, only on node
|
||||
ipcRenderer.send('listFiles', dir);
|
||||
} else {
|
||||
//no NODE integration, propably browser access
|
||||
$("div:has(#files)").hide();//Disable file system preview
|
||||
mainMenuItems.slice("files");
|
||||
var wifi, wifiQuality;
|
||||
|
||||
ipcRenderer.on('wifi', (event, arg) => {
|
||||
wifi = arg;
|
||||
wifi = wifi;
|
||||
});
|
||||
|
||||
ipcRenderer.on('wifiQuality', (event, arg) => {
|
||||
wifiQuality = arg;
|
||||
wifiQuality = wifiQuality;
|
||||
});
|
||||
|
||||
ipcRenderer.on('fullscreen', (event, arg) => {
|
||||
console.log("Fullscreen now " + arg);
|
||||
settingMenuItemValues[0] = arg;
|
||||
});
|
||||
|
||||
ipcRenderer.send('get-data', 'wifi');
|
||||
ipcRenderer.send('get-data', 'wifiQuality');
|
||||
|
||||
function sendKeys(arg, arg2){
|
||||
ipcRenderer.send('sendKeys', arg, arg2);
|
||||
return true;
|
||||
}
|
||||
var loc = window.location.pathname;
|
||||
var dir = loc.substring(1, loc.lastIndexOf('/'));
|
||||
ipcRenderer.send('listFiles', dir);
|
||||
|
|
@ -13,10 +13,8 @@ function updateIndicator() {
|
|||
} else {
|
||||
$('.info i').html('signal_wifi_0_bar');
|
||||
}
|
||||
console.log('Wifi quality is ' + wifiQuality + '%')
|
||||
} else {
|
||||
$('.info i').html('signal_wifi_off');
|
||||
console.log('offline');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
var start = true;
|
||||
var screen = 0;
|
||||
var menu = 0;
|
||||
var gamepads = 0;
|
||||
|
||||
const gamepad = new Gamepad();
|
||||
var toastCancel;
|
||||
var today = new Date();
|
||||
var time = today.getHours() + ":" + today.getMinutes();
|
||||
function pad(num, size){ return ('00000000000000' + num).substr(-size); }
|
||||
|
|
@ -15,90 +11,27 @@ setInterval((function(){
|
|||
$("#time").html(time);
|
||||
}), 1000);
|
||||
|
||||
function toast(string) {
|
||||
clearTimeout(toastCancel);
|
||||
var x = document.getElementById("snackbar");
|
||||
|
||||
x.className = "show";
|
||||
x.innerHTML = string;
|
||||
toastCancel = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 3000);
|
||||
}
|
||||
function showWelcomeText(){
|
||||
var x = document.getElementById("welcome-continue");
|
||||
if(gamepads > 0){
|
||||
x.innerHTML = "Press <b>START</b> to procceed";
|
||||
} else {
|
||||
x.innerHTML = "Connect your gamepad!";
|
||||
}
|
||||
}
|
||||
function showMainMenu(){
|
||||
window.start = false;
|
||||
window.screen = 1;
|
||||
window.menu = 1;
|
||||
goRight();
|
||||
goLeft();
|
||||
|
||||
$(".game").width((function(offset, width){
|
||||
return $(".game .game-preview:eq(" + offset + ")").width();
|
||||
}));
|
||||
|
||||
$("#main-menu").removeClass('hidden');
|
||||
$("#selection").removeClass("hidden");
|
||||
$(".welcome-main").addClass("hide");
|
||||
setTimeout(1000, (function(){$(".welcome-main").hide();}));
|
||||
}
|
||||
function showStartMenu(){
|
||||
window.start = true;
|
||||
window.screen = 0;
|
||||
window.menu = 0;
|
||||
$("#main-menu").addClass('hidden');
|
||||
$("#selection").addClass("hidden");
|
||||
$(".welcome-main").removeClass("hide");
|
||||
$(".welcome-main").show();
|
||||
}
|
||||
gamepad.on('connect', e => {
|
||||
gamepads++;
|
||||
showWelcomeText();
|
||||
toast(`Player ${e.index + 1} has connected`);
|
||||
console.log(`controller ${e.index} connected!`);
|
||||
});
|
||||
|
||||
gamepad.on('disconnect', e => {
|
||||
toast(`Player ${e.index + 1} has disconnected`);
|
||||
gamepads --;
|
||||
console.log(`controller ${e.index} disconnected!`);
|
||||
});
|
||||
|
||||
gamepad.on('press', 'start', () => {
|
||||
showMainMenu();
|
||||
});
|
||||
|
||||
gamepad.on('press', 'd_pad_left', () => {
|
||||
goLeft();
|
||||
});
|
||||
gamepad.on('press', 'd_pad_right', () => {
|
||||
goRight();
|
||||
});
|
||||
gamepad.on('press', 'button_1', () => {
|
||||
select();
|
||||
});
|
||||
gamepad.on('press', 'button_2', () => {
|
||||
deselect();
|
||||
});
|
||||
function goLeft(){
|
||||
if(menu == 1 || menu == 2 || menu == 3){
|
||||
selected--;
|
||||
callMenuItem();
|
||||
}
|
||||
console.log("Going left");
|
||||
}
|
||||
function goRight(){
|
||||
if(menu == 1 || menu == 2 || menu == 3){
|
||||
selected++;
|
||||
callMenuItem();
|
||||
}
|
||||
console.log("Going right");
|
||||
}
|
||||
|
||||
var leftCounter = 0;
|
||||
var rightCounter = 0;
|
||||
|
|
|
|||
1
html/styles.min.css
vendored
1
html/styles.min.css
vendored
File diff suppressed because one or more lines are too long
479
html/styles.scss
479
html/styles.scss
|
|
@ -1,479 +0,0 @@
|
|||
/*
|
||||
* VARS
|
||||
*/
|
||||
$unselected: rgb(182,182,182);
|
||||
$selected: white;
|
||||
/*
|
||||
* FONTS
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Open sans';
|
||||
src: url('https://console.danbulant.eu/open-sans/OpenSans-Regular.ttf');//online version
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Gilroy-bold';
|
||||
src: url('https://console.danbulant.eu/Gilroy-ExtraBold.otf');//online version, if you want offline, put fonts in this folder and change the link here
|
||||
}
|
||||
body {
|
||||
background: #5d75ad;
|
||||
background: linear-gradient(216deg, #6b89cf, #3a3766);
|
||||
background-size: 400% 400%;
|
||||
|
||||
-webkit-animation: BackgroundGradient 33s ease infinite;
|
||||
-moz-animation: BackgroundGradient 33s ease infinite;
|
||||
-o-animation: BackgroundGradient 33s ease infinite;
|
||||
animation: BackgroundGradient 33s ease infinite;
|
||||
}
|
||||
/*
|
||||
* Background
|
||||
*/
|
||||
@-webkit-keyframes BackgroundGradient {
|
||||
0%{background-position:0% 22%}
|
||||
50%{background-position:100% 79%}
|
||||
100%{background-position:0% 22%}
|
||||
}
|
||||
@-moz-keyframes BackgroundGradient {
|
||||
0%{background-position:0% 22%}
|
||||
50%{background-position:100% 79%}
|
||||
100%{background-position:0% 22%}
|
||||
}
|
||||
@-o-keyframes BackgroundGradient {
|
||||
0%{background-position:0% 22%}
|
||||
50%{background-position:100% 79%}
|
||||
100%{background-position:0% 22%}
|
||||
}
|
||||
@keyframes BackgroundGradient {
|
||||
0%{background-position:0% 22%}
|
||||
50%{background-position:100% 79%}
|
||||
100%{background-position:0% 22%}
|
||||
}
|
||||
/*
|
||||
* Main menu
|
||||
*/
|
||||
|
||||
.main {
|
||||
display: block;
|
||||
position: fixed;
|
||||
bottom: 40px;
|
||||
left: 10px;
|
||||
.button-main-container {
|
||||
display: inline-block;
|
||||
height: 120px;
|
||||
.button-main {
|
||||
transition: 0.5s margin-top;
|
||||
margin-top: 20px;
|
||||
color: $unselected;
|
||||
span {
|
||||
padding-left: 10px;
|
||||
}
|
||||
.button-menu {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
padding-left: 45px;
|
||||
padding-right: 45px;
|
||||
border-radius: 5px;
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
color: white;
|
||||
background: #414E92;
|
||||
.material-icons {
|
||||
font-size: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.button-main-container .button-main.selected{
|
||||
color: white;
|
||||
margin-top: 0px;
|
||||
.button-menu{
|
||||
background: #3D7BFF;
|
||||
box-shadow: 0px 0px 5px rgba(61, 123, 255, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 95%;
|
||||
}
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
}
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
.title-font {
|
||||
font-family: 'Gilroy-bold', 'Open Sans', sans-serif;
|
||||
}
|
||||
/*
|
||||
* Tab views
|
||||
*/
|
||||
#selection {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
left: 15px;
|
||||
width: calc(100% - 15px);
|
||||
.menu-item {
|
||||
color: white;
|
||||
|
||||
h1 {
|
||||
font-family: 'Gilroy-bold', 'Open Sans', sans-serif;
|
||||
padding: 10px;
|
||||
font-size: 50px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
#games-view {
|
||||
.previews {
|
||||
height: 150px;
|
||||
|
||||
.game {
|
||||
|
||||
height: auto;
|
||||
text-align: justify;
|
||||
height: 150px;
|
||||
.game-preview {
|
||||
height: 130px;
|
||||
border-radius: 5px;
|
||||
// Now in JS
|
||||
// -webkit-transition: 0.5s width, 0.5s height, 0.5s max-height, 0.3s top, 0.3s left, 0.5s opacity;
|
||||
// -moz-transition: 0.5s width, 0.5s height, 0.5s max-height, 0.3s top, 0.3s left, 0.5s opacity;
|
||||
// transition: 0.5s width, 0.5s height, 0.5s max-height, 0.3s top, 0.3s left, 0.5s opacity;
|
||||
}
|
||||
font-size: 20px;
|
||||
display: inline-block;
|
||||
color: $unselected;
|
||||
opacity: 1;
|
||||
// -webkit-transition: 0.5s width, 0.5s height, 0.5s max-height, 0.3s top, 0.3s left, 0.5s opacity;
|
||||
// -moz-transition: 0.5s width, 0.5s height, 0.5s max-height, 0.3s top, 0.3s left, 0.5s opacity;
|
||||
// transition: 0.5s width, 0.5s height, 0.5s max-height, 0.3s top, 0.3s left, 0.5s opacity;
|
||||
}
|
||||
.game.selected {
|
||||
height: 190px;
|
||||
color: $selected;
|
||||
.game-preview {
|
||||
height: 170px;
|
||||
-webkit-box-shadow: 0px 27px 72px -32px rgba(0,0,0,0.51);
|
||||
-moz-box-shadow: 0px 27px 72px -32px rgba(0,0,0,0.51);
|
||||
box-shadow: 0px 27px 72px -32px rgba(0,0,0,0.51);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#files-view {
|
||||
overflow-y: hidden;
|
||||
overflow-x: hidden;
|
||||
height: 500px;
|
||||
#files-list-container {
|
||||
::-webkit-scrollbar {
|
||||
width: 7px;
|
||||
}
|
||||
::-webkit-scrollbar-button {
|
||||
height: 0px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(255,255,255,0.2);
|
||||
border-radius: 50px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(255,255,255,0.4);
|
||||
}
|
||||
::-webkit-scrollbar-thumb:active {
|
||||
background: rgba(255,255,255,0.5);
|
||||
}
|
||||
width: 100%;
|
||||
height: 95%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
margin: 0;
|
||||
ul {
|
||||
border-top: 1px solid rgba(255,255,255,0.2);
|
||||
border-bottom: 1px solid rgba(255,255,255,0.2);
|
||||
height: 80%;
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
list-style-type: none;
|
||||
padding-left: 0;
|
||||
li {
|
||||
vertical-align: middle;
|
||||
padding-top: 11px;
|
||||
padding-left: 15px;
|
||||
height: 36px;
|
||||
}
|
||||
li:not(:first-child){
|
||||
border-top: 1px solid rgba(255,255,255,0.2);
|
||||
}
|
||||
.selected {
|
||||
background: rgba(255,255,255,0.4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Information panel, marked as 'nav'
|
||||
*/
|
||||
nav {
|
||||
z-index:999;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 10px;
|
||||
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 30px;
|
||||
width: calc(100% - 20px);
|
||||
.profile {
|
||||
left: 0;
|
||||
}
|
||||
.info {
|
||||
right: 0;
|
||||
}
|
||||
div {
|
||||
display: inline;
|
||||
color: white;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Dialog
|
||||
*/
|
||||
#dialog {
|
||||
display: block;
|
||||
position: fixed;
|
||||
z-index: 998;
|
||||
|
||||
}
|
||||
/*
|
||||
* Account
|
||||
*/
|
||||
#account-view {
|
||||
button {
|
||||
color: white;
|
||||
background-color: transparent;
|
||||
border: 1px solid white;
|
||||
border-radius: 40px;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Settings page
|
||||
*/
|
||||
#settings-view {
|
||||
.option {
|
||||
color: $unselected;
|
||||
}
|
||||
.option.selected {
|
||||
color: $selected;
|
||||
}
|
||||
.option.switchable {
|
||||
.switch .slider{
|
||||
width: 52px;
|
||||
height: 25px;
|
||||
}
|
||||
.switch .slider:before {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Startup
|
||||
*/
|
||||
.welcome-main {
|
||||
color: white;
|
||||
width: 500px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
position: relative;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
//fade out transition
|
||||
-webkit-transition: opacity 1s ease-in-out;
|
||||
-moz-transition: opacity 1s ease-in-out;
|
||||
-ms-transition: opacity 1s ease-in-out;
|
||||
-o-transition: opacity 1s ease-in-out;
|
||||
transition: opacity 1s ease-in-out;
|
||||
h1 {
|
||||
font-size: 100px;
|
||||
margin: 0;
|
||||
}
|
||||
h5 {
|
||||
margin-top: 0;
|
||||
b {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
.welcome-main.hide {
|
||||
opacity: 0;
|
||||
}
|
||||
.glow {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
-webkit-animation: glow 1s ease-in-out infinite alternate;
|
||||
-moz-animation: glow 1s ease-in-out infinite alternate;
|
||||
animation: glow 1s ease-in-out infinite alternate;
|
||||
}
|
||||
$innerGlowColor: #fff;
|
||||
$outerGlowColorLight: #fff;//#e60073
|
||||
$outerGlowColorDark: #969696;//#ff4da6
|
||||
$GlowSize: 1px;
|
||||
@-webkit-keyframes glow {
|
||||
from {
|
||||
text-shadow: 0 0 $GlowSize*1 $innerGlowColor, 0 0 $GlowSize*2 $innerGlowColor, 0 0 $GlowSize*3 $outerGlowColorLight, 0 0 $GlowSize*4 $outerGlowColorLight, 0 0 $GlowSize*5 $outerGlowColorLight, 0 0 $GlowSize*6 $outerGlowColorLight, 0 0 $GlowSize*7 $outerGlowColorLight;
|
||||
}
|
||||
to {
|
||||
text-shadow: 0 0 $GlowSize*2 $innerGlowColor, 0 0 $GlowSize*3 $outerGlowColorDark, 0 0 $GlowSize*4 $outerGlowColorDark, 0 0 $GlowSize*5 $outerGlowColorDark, 0 0 $GlowSize*6 $outerGlowColorDark, 0 0 $GlowSize*7 $outerGlowColorDark, 0 0 $GlowSize*8 $outerGlowColorDark;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* UI
|
||||
*/
|
||||
$MainColor: #fff;
|
||||
$SecondaryColor: #fff;
|
||||
$OutlineColor: rgb(196, 213, 1);
|
||||
.back-arrow {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
left: 20px;
|
||||
color: $OutlineColor;
|
||||
.circle {
|
||||
border-radius: 50%;
|
||||
border: 1px solid $OutlineColor;
|
||||
}
|
||||
vertical-align: middle;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
letter-spacing: normal;
|
||||
text-transform: none;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
word-wrap: normal;
|
||||
direction: ltr;
|
||||
-webkit-font-feature-settings: 'liga';
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
/*
|
||||
* Snackbar / toast messages
|
||||
*/
|
||||
/* The snackbar - position it at the bottom and in the middle of the screen */
|
||||
#snackbar {
|
||||
visibility: hidden; /* Hidden by default. Visible on click */
|
||||
min-width: 250px; /* Set a default minimum width */
|
||||
margin-left: -125px; /* Divide value of min-width by 2 */
|
||||
//background-color: #333; /* Black background color */
|
||||
color: #fff; /* White text color */
|
||||
text-align: center; /* Centered text */
|
||||
border-radius: 2px; /* Rounded borders */
|
||||
padding: 16px; /* Padding */
|
||||
position: fixed; /* Sit on top of the screen */
|
||||
z-index: 100; /* Add a z-index if needed */
|
||||
left: 50%; /* Center the snackbar */
|
||||
bottom: 15px; /* 30px from the bottom */
|
||||
}
|
||||
|
||||
/* Show the snackbar when clicking on a button (class added with JavaScript) */
|
||||
#snackbar.show {
|
||||
visibility: visible; /* Show the snackbar */
|
||||
/* Add animation: Take 0.5 seconds to fade in and out the snackbar.
|
||||
However, delay the fade out process for 2.5 seconds */
|
||||
-webkit-animation: snackbarFadein 0.5s, snackbarFadeout 0.5s 2.5s;
|
||||
animation: snackbarFadein 0.5s, snackbarFadeout 0.5s 2.5s;
|
||||
}
|
||||
|
||||
/* Animations to fade the snackbar in and out */
|
||||
@-webkit-keyframes snackbarFadein {
|
||||
from {bottom: 0; opacity: 0;}
|
||||
to {bottom: 30px; opacity: 1;}
|
||||
}
|
||||
|
||||
@keyframes snackbarFadein {
|
||||
from {bottom: 0; opacity: 0;}
|
||||
to {bottom: 30px; opacity: 1;}
|
||||
}
|
||||
|
||||
@-webkit-keyframes snackbarFadeout {
|
||||
from {bottom: 30px; opacity: 1;}
|
||||
to {bottom: 0; opacity: 0;}
|
||||
}
|
||||
|
||||
@keyframes snackbarFadeout {
|
||||
from {bottom: 30px; opacity: 1;}
|
||||
to {bottom: 0; opacity: 0;}
|
||||
}
|
||||
/*
|
||||
* Switches / sliders
|
||||
*/
|
||||
/* The switch - the box around the slider */
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
/* Hide default HTML checkbox */
|
||||
.switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/* The slider */
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: white;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: #2196F3;
|
||||
}
|
||||
|
||||
input:focus + .slider {
|
||||
box-shadow: 0 0 1px #2196F3;
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
-webkit-transform: translateX(26px);
|
||||
-ms-transform: translateX(26px);
|
||||
transform: translateX(26px);
|
||||
}
|
||||
|
||||
/* Rounded sliders */
|
||||
.slider.round {
|
||||
border-radius: 34px;
|
||||
}
|
||||
|
||||
.slider.round:before {
|
||||
border-radius: 50%;
|
||||
}
|
||||
33
html/utils.js
Normal file
33
html/utils.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
const User = require("./views/data/user");
|
||||
const Card = require("./views/data/card");
|
||||
|
||||
function removeChildNodes(node){
|
||||
while (node.firstChild) {
|
||||
node.removeChild(node.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
function getUsers() {
|
||||
var user = new User;
|
||||
user.name = "Add";
|
||||
user.avatar = "../../dist/icons/plus.png";
|
||||
|
||||
var user2 = new User;
|
||||
user2.name = "Test";
|
||||
user2.avatar = "https://api.adorable.io/avatars/285/test";
|
||||
|
||||
return [user, user2];
|
||||
}
|
||||
|
||||
function usersToCards(users){
|
||||
var cards = [];
|
||||
users.forEach((u, i)=>{
|
||||
cards[i] = u.getCard();
|
||||
})
|
||||
return cards;
|
||||
}
|
||||
module.exports = {
|
||||
removeChildNodes,
|
||||
getUsers,
|
||||
usersToCards
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
01
|
||||
9
html/views/data/achievment.js
Normal file
9
html/views/data/achievment.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
class Achievment {
|
||||
time = new Date;
|
||||
name = "";
|
||||
game = 0;
|
||||
image = "";
|
||||
text = "";
|
||||
}
|
||||
|
||||
module.exports = Achievment;
|
||||
6
html/views/data/action.js
Normal file
6
html/views/data/action.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
class Action {
|
||||
button = null;
|
||||
text = "";
|
||||
}
|
||||
|
||||
module.exports = Action;
|
||||
7
html/views/data/button.js
Normal file
7
html/views/data/button.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
class Button {
|
||||
icon = "";
|
||||
player = -1;
|
||||
type = "";
|
||||
}
|
||||
|
||||
module.exports = Button;
|
||||
8
html/views/data/card.js
Normal file
8
html/views/data/card.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
class Card {
|
||||
name = "";
|
||||
image = "";
|
||||
action = "";
|
||||
onclick(){}
|
||||
}
|
||||
|
||||
module.exports = Card;
|
||||
7
html/views/data/currentUser.js
Normal file
7
html/views/data/currentUser.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
const User = require("./user");
|
||||
|
||||
class CurrentUser extends User {
|
||||
|
||||
}
|
||||
|
||||
module.exports = CurrentUser;
|
||||
13
html/views/data/game.js
Normal file
13
html/views/data/game.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
class Game {
|
||||
name = "";
|
||||
image = "";
|
||||
run(){
|
||||
console.log("Game running");
|
||||
this.exit();
|
||||
}
|
||||
exit(){
|
||||
global.menu.show();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Game;
|
||||
20
html/views/data/user.js
Normal file
20
html/views/data/user.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
const Card = require("./card");
|
||||
|
||||
class User {
|
||||
name = [];
|
||||
username = "";
|
||||
friends = [];
|
||||
avatar = "";
|
||||
achievments = {};
|
||||
|
||||
getCard(){
|
||||
var card = new Card;
|
||||
card.name = this.name;
|
||||
card.image = this.avatar;
|
||||
card.action = false;
|
||||
|
||||
return card;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = User;
|
||||
31
html/views/index.html
Normal file
31
html/views/index.html
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Console hub</title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"><!-- include material icons -->
|
||||
<link rel="stylesheet" href="../../dist/css/styles.min.css"><!-- include custom stylesheet -->
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self' https://fonts.googleapis.com http://dist.danbulant.eu 'unsafe-inline';
|
||||
font-src *;
|
||||
img-src *">
|
||||
</head>
|
||||
<body>
|
||||
<div class='box'>
|
||||
<div class='wave -one'></div>
|
||||
<div class='wave -two'></div>
|
||||
<div class='wave -three'></div>
|
||||
</div>
|
||||
|
||||
<div id="view">
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
if (typeof module === 'object') {window.module = module; module = undefined;}
|
||||
</script>
|
||||
<script src="../dep/jquery.min.js"></script>
|
||||
<script>if (window.module) module = window.module;</script>
|
||||
<script src="main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
7
html/views/main.js
Normal file
7
html/views/main.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
const gamepadConnected = require("./menu/connected");
|
||||
const welcome = require("./menu/welcome");
|
||||
|
||||
console.log("Console-hub alpha");
|
||||
|
||||
welcome();
|
||||
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
{
|
||||
"short_name": "Console hub",
|
||||
"name": "Console hub beta",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/favicon.ico",
|
||||
"type": "image/ico"
|
||||
}
|
||||
],
|
||||
"start_url": "/",
|
||||
"background_color": "#5d75ad",
|
||||
"display": "standalone",
|
||||
"orientation": "landscape",
|
||||
"scope": "/",
|
||||
"theme_color": "#5d75ad"
|
||||
}
|
||||
{
|
||||
"short_name": "Console hub",
|
||||
"name": "Console hub beta",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/favicon.ico",
|
||||
"type": "image/ico"
|
||||
}
|
||||
],
|
||||
"start_url": "/",
|
||||
"background_color": "#5d75ad",
|
||||
"display": "standalone",
|
||||
"orientation": "landscape",
|
||||
"scope": "/",
|
||||
"theme_color": "#5d75ad"
|
||||
}
|
||||
67
html/views/menu/connected.js
Normal file
67
html/views/menu/connected.js
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
const utils = require("../../utils");
|
||||
const gui = require("./gui");
|
||||
const Gamepad = require("../../gamepad/gamepad");
|
||||
const gamepad = new Gamepad;
|
||||
const controller = require("../../gamepad/ps4controller");
|
||||
|
||||
function gamepadConnected(){
|
||||
console.log("Gamepad connected");
|
||||
|
||||
var view = document.getElementById("view");
|
||||
utils.removeChildNodes(view);
|
||||
|
||||
gui.showTitle("Your profile");
|
||||
gui.showDescription("Select or add your Pushr account to continue");
|
||||
|
||||
const users = utils.getUsers();
|
||||
var max = users.length - 1;
|
||||
var cards = utils.usersToCards(users);
|
||||
gui.renderUserCardList(cards);
|
||||
var selected = 0;
|
||||
|
||||
if(controller.isConnected()){
|
||||
controller.setExtras({
|
||||
rumbleLeft: 0, // 0-255 (Rumble left intensity)
|
||||
rumbleRight: 0, // 0-255 (Rumble right intensity)
|
||||
red: 0, // 0-255 (Red intensity)
|
||||
green: 75, // 0-255 (Blue intensity)
|
||||
blue: 225, // 0-255 (Green intensity)
|
||||
});
|
||||
}
|
||||
gamepad.on("press", "d_pad_right", () => {
|
||||
if(selected < max){
|
||||
selected++;
|
||||
gui.renderSelectedUserCard(selected, selected - 1);
|
||||
}
|
||||
});
|
||||
gamepad.on("press", "d_pad_left", () => {
|
||||
if (selected > 0){
|
||||
selected--;
|
||||
gui.renderSelectedUserCard(selected, selected + 1);
|
||||
}
|
||||
});
|
||||
gamepad.on("press", "stick_axis_left", e => {
|
||||
if(e.value[0] < -0.3){
|
||||
if (selected > 0){
|
||||
selected--;
|
||||
gui.renderSelectedUserCard(selected, selected + 1);
|
||||
}
|
||||
} else if (e.value[0] > 0.3) {
|
||||
if (selected < max) {
|
||||
selected++;
|
||||
gui.renderSelectedUserCard(selected, selected - 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
gamepad.on("press", "button_1", () => {
|
||||
gamepad.off("press", "d_pad_left");
|
||||
gamepad.off("press", "d_pad_right");
|
||||
gamepad.off("press", "stick_axis_left");
|
||||
gamepad.off("press", "stick_axis_right");
|
||||
|
||||
console.log("Loged in as ", users[selected]);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = gamepadConnected;
|
||||
105
html/views/menu/gui.js
Normal file
105
html/views/menu/gui.js
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
|
||||
function showTitle(title) {
|
||||
var view = document.getElementById("view");
|
||||
var t = document.createElement("h1");
|
||||
t.classList.add("title--main");
|
||||
t.innerText = title;
|
||||
|
||||
view.appendChild(t);
|
||||
}
|
||||
|
||||
function showTitleInMiddle(title) {
|
||||
var view = document.getElementById("view");
|
||||
var t = document.createElement("h1");
|
||||
t.classList.add("title--main");
|
||||
t.classList.add("middle");
|
||||
t.innerText = title;
|
||||
|
||||
view.appendChild(t);
|
||||
|
||||
t.style.marginLeft = "-" + (t.offsetWidth / 2) + "px";
|
||||
}
|
||||
|
||||
function showDescription(desc) {
|
||||
var view = document.getElementById("view");
|
||||
var t = document.createElement("h2");
|
||||
t.classList.add("title--description");
|
||||
t.innerText = desc;
|
||||
|
||||
view.appendChild(t);
|
||||
}
|
||||
function showDescriptionInMiddle(desc) {
|
||||
var view = document.getElementById("view");
|
||||
var t = document.createElement("h2");
|
||||
t.classList.add("title--description");
|
||||
t.classList.add("middle");
|
||||
t.innerText = desc;
|
||||
|
||||
view.appendChild(t);
|
||||
|
||||
t.style.marginLeft = "-" + (t.offsetWidth / 2) + "px";
|
||||
t.style.marginTop = (document.querySelector(".title--main").offsetHeight) + "px";
|
||||
}
|
||||
|
||||
global.cards = [];
|
||||
global.currentCard = -1;
|
||||
function getCard(card){
|
||||
var t = document.createElement("div");
|
||||
var title = document.createElement("span");
|
||||
title.innerText = card.name;
|
||||
var image = document.createElement("img");
|
||||
image.src = card.image;
|
||||
global.cards[global.cards.length] = card;
|
||||
|
||||
t.appendChild(title);
|
||||
t.appendChild(image);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
function renderSelectedUserCard(selected, previous) {
|
||||
var prev = document.querySelectorAll(".user-cards-list .card")[previous];
|
||||
prev.classList.remove("selected");
|
||||
var card = document.querySelectorAll(".user-cards-list .card")[selected];
|
||||
card.classList.add("selected");
|
||||
}
|
||||
|
||||
function renderUserCardList(cards){
|
||||
var view = document.getElementById("view");
|
||||
var list = document.createElement("div");
|
||||
|
||||
view.appendChild(list);
|
||||
|
||||
list.classList.add("user-cards-list");
|
||||
|
||||
cards.forEach((card, i)=>{
|
||||
var c = document.createElement("div");
|
||||
c.classList.add("card");
|
||||
|
||||
var img = document.createElement("img");
|
||||
img.src = card.image;
|
||||
c.appendChild(img);
|
||||
|
||||
var text = document.createElement("p");
|
||||
text.innerText = card.name;
|
||||
text.classList.add("name");
|
||||
c.appendChild(text);
|
||||
|
||||
list.appendChild(c);
|
||||
});
|
||||
renderSelectedUserCard(0,0);
|
||||
}
|
||||
|
||||
function showAction(action, side){
|
||||
|
||||
}
|
||||
module.exports = {
|
||||
showTitle,
|
||||
showDescription,
|
||||
getCard,
|
||||
renderUserCardList,
|
||||
showAction,
|
||||
renderSelectedUserCard,
|
||||
showTitleInMiddle,
|
||||
showDescriptionInMiddle
|
||||
}
|
||||
18
html/views/menu/welcome.js
Normal file
18
html/views/menu/welcome.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
const utils = require("../../utils");
|
||||
const gui = require("./gui");
|
||||
const Gamepad = require("../../gamepad/gamepad");
|
||||
const gamepad = new Gamepad;
|
||||
const connected = require("./connected");
|
||||
|
||||
function welcome(){
|
||||
gui.showTitleInMiddle("Welcome to Console-hub");
|
||||
gui.showDescriptionInMiddle("Press home button to start");
|
||||
|
||||
gamepad.on("press", "vendor", ()=>{
|
||||
gamepad.off('press', 'vendor');
|
||||
|
||||
connected();
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = welcome;
|
||||
147
html/views/styles.scss
Normal file
147
html/views/styles.scss
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* VARS
|
||||
*/
|
||||
$unselected: rgb(182,182,182);
|
||||
$selected: white;
|
||||
/*
|
||||
* FONTS
|
||||
*/
|
||||
@import url('https://fonts.googleapis.com/css?family=Open+Sans&display=swap');
|
||||
@font-face {
|
||||
font-family: 'Gilroy-bold';
|
||||
src: url('../../dist/fonts/Gilroy-ExtraBold.otf');//online version, if you want offline, put fonts in this folder and change the link here
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #0e6cc4;
|
||||
overflow-x:hidden;
|
||||
overflow-y:hidden;
|
||||
}
|
||||
|
||||
.title--main {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: fixed;
|
||||
left: 45px;
|
||||
top: 100px;
|
||||
font-size: 4rem;
|
||||
color: white;
|
||||
font-family: 'Gilroy-bold';
|
||||
}
|
||||
.view {
|
||||
width: 100%;
|
||||
}
|
||||
.title--description {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: fixed;
|
||||
left: 80px;
|
||||
top: 175px;
|
||||
font-size: 1.3rem;
|
||||
color: white;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
}
|
||||
|
||||
.title--main.middle, .title--description.middle {
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.user-cards-list {
|
||||
z-index: 99;
|
||||
width: 100%;
|
||||
height: 30vh;
|
||||
margin-top: 30vh;
|
||||
display: flex;
|
||||
.card {
|
||||
z-index: 99;
|
||||
margin: 5px;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
width: 30vh;
|
||||
height: 30vh;
|
||||
background-size: 30vh 30vh;
|
||||
font-size: 2rem;
|
||||
color: white;
|
||||
border: 5px solid transparent;
|
||||
.name {
|
||||
margin-top: -15px;
|
||||
padding-left: 5px;
|
||||
font-size: 43px;
|
||||
}
|
||||
img {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.card.selected {
|
||||
// margin-top: -5px;
|
||||
// margin-left: -5px;
|
||||
border: 5px solid white;
|
||||
}
|
||||
.card:first-child {
|
||||
margin-left: auto;
|
||||
}
|
||||
.card:last-child {
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.box {
|
||||
z-index: 1;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
transform: rotate(80deg);
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.wave {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: .4;
|
||||
position: absolute;
|
||||
top: 3%;
|
||||
left: 10%;
|
||||
background: #0af;
|
||||
width: 3000px;
|
||||
height: 2600px;
|
||||
margin-left: -150px;
|
||||
margin-top: -250px;
|
||||
transform-origin: 50% 48%;
|
||||
border-radius: 43%;
|
||||
animation: drift 9000ms infinite linear;
|
||||
}
|
||||
|
||||
.wave.-three {
|
||||
animation: drift 8500ms infinite linear;
|
||||
position: fixed;
|
||||
background-color: #77daff;
|
||||
}
|
||||
|
||||
.wave.-two {
|
||||
animation: drift 8000ms infinite linear;
|
||||
opacity: .1;
|
||||
background: black;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.box:after {
|
||||
content: '';
|
||||
display: block;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 11;
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
@keyframes drift {
|
||||
from { transform: rotate(0deg); }
|
||||
from { transform: rotate(360deg); }
|
||||
}
|
||||
36
main.js
36
main.js
|
|
@ -1,4 +1,5 @@
|
|||
const { dialog, Menu, app, BrowserWindow } = require('electron');
|
||||
require('electron-reload')(__dirname);
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var files = require('./files');
|
||||
|
|
@ -27,14 +28,13 @@ function createWindow () {
|
|||
width: 800,
|
||||
height: 600,
|
||||
frame: true,
|
||||
fullScreen: true,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
nodeIntegrationInWorker: true,
|
||||
webSecurity: false
|
||||
nodeIntegrationInWorker: true
|
||||
}
|
||||
})
|
||||
win.setTitle('Console hub (ALPHA) DEV');
|
||||
win.setProgressBar(1.1);
|
||||
win.setTitle('Console hub (ALPHA)');
|
||||
win.setFullScreenable(true);
|
||||
var template = [{
|
||||
label: "Application",
|
||||
|
|
@ -47,7 +47,7 @@ function createWindow () {
|
|||
buttons: ['ok'],
|
||||
title: 'About app',
|
||||
message: 'Console hub (ALPHA) dev version.',
|
||||
detail: 'Nightly developer version, alpha stage. Accounts disabled.\n(c) Daniel Bulant',
|
||||
detail: 'Nightly developer version, alpha stage. \n(c) Daniel Bulant',
|
||||
};
|
||||
dialog.showMessageBox(null, options, (response) => {
|
||||
|
||||
|
|
@ -124,17 +124,16 @@ function createWindow () {
|
|||
const menu = Menu.buildFromTemplate(template);
|
||||
win.setAutoHideMenuBar(true);
|
||||
Menu.setApplicationMenu(menu);
|
||||
win.loadFile('html/index.html')
|
||||
win.webContents.once('dom-ready', () => {
|
||||
// win.webContents.openDevTools() //debug tools
|
||||
win.setProgressBar(0);
|
||||
})
|
||||
win.loadFile('html/views/index.html')
|
||||
|
||||
win.maximize();
|
||||
|
||||
win.on('enter-full-screen', () => {
|
||||
console.log('Entered fullscreen');
|
||||
win.webContents.send('fullscreen', true);
|
||||
win.setMenuBarVisibility(false)
|
||||
})
|
||||
|
||||
win.on('enter-full-html-screen', () => {
|
||||
console.log('Entered html fullscreen');
|
||||
win.webContents.send('fullscreen', true);
|
||||
|
|
@ -150,9 +149,11 @@ function createWindow () {
|
|||
win.webContents.send('fullscreen', false);
|
||||
win.setMenuBarVisibility(true)
|
||||
})
|
||||
|
||||
win.on('closed', () => {
|
||||
win = null
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
app.on('ready', createWindow)
|
||||
|
|
@ -166,13 +167,22 @@ ipcMain.on('get-data', (event, arg) => {
|
|||
event.reply('wifiQuality', wifiQuality);
|
||||
}
|
||||
})
|
||||
ipcMain.on('set-load', (event, arg) => {
|
||||
win.setProgressBar(arg);
|
||||
})
|
||||
|
||||
ipcMain.on('fullscreen', (event, arg) => {
|
||||
console.log('Setting to fullscreen ' + arg);
|
||||
win.setFullScreen(arg);
|
||||
})
|
||||
|
||||
function sendKeybinding (win, modifiers, keyCode) {
|
||||
win.webContents.sendInputEvent({ type: 'keyDown', modifiers: modifiers, keyCode: keyCode })
|
||||
win.webContents.sendInputEvent({ type: 'char', modifiers: modifiers, keyCode: keyCode })
|
||||
win.webContents.sendInputEvent({ type: 'keyUp', modifiers: modifiers, keyCode: keyCode })
|
||||
}
|
||||
|
||||
ipcMain.on('sendKeys', (event, modifiers, keyCode) => {
|
||||
console.log('Sending keys: '+keyCode + " with modifiers " + modifiers);
|
||||
sendKeybinding(win, modifiers, keyCode);
|
||||
})
|
||||
ipcMain.on('listFiles', (event, arg) => {
|
||||
console.log('Listing all files and directories in ' + arg);
|
||||
dirs = [];
|
||||
|
|
|
|||
2840
package-lock.json
generated
Normal file
2840
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "console-hub",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"start": "electron ."
|
||||
|
|
@ -16,6 +16,8 @@
|
|||
"electron-packager": "^14.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"electron-reload": "^1.5.0",
|
||||
"node-hid": "^1.0.0",
|
||||
"node-wifi": "^2.0.5"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue