Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Yoke now won't send floats, or extra information. #35

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,6 @@ The communication between the Linux client and the Android app are unencrypted U

## Tweaking

Many aspects of Yoke behavior can be changed easily - ave a look at `yoke/assets/joypad`, `bin/yoke` and `yoke/service.py`.
Many aspects of Yoke behavior can be changed easily - have a look at `yoke/assets/joypad`, `bin/yoke` and `yoke/service.py`.

![Thumbstick](media/thumbstick.gif)
Binary file modified devel/app-debug.apk
Binary file not shown.
41 changes: 36 additions & 5 deletions yoke/assets/joypad/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,24 @@ div {
background-size: 100% 100%;
}

/* Joysticks */
.joystick { background-image: url("img/joystick.svg"); background-color: #bbb; }
.circle {
background-color: black;
width: 10px;
height: 10px;
border-radius: 100%;
}

/* Buttons */
.button { background-color: #bbb; }
.pressed { filter: brightness(70%); }
/* seq 1 16 | xargs -I xx echo "#bxx { background-image: url('img/xx.svg'); }" */
#b1 { background-image: url('img/1.svg'); }
#b2 { background-image: url('img/2.svg'); }
#b3 { background-image: url('img/3.svg'); }
#b4 { background-image: url('img/4.svg'); }
/* Some edits done by hand */
#b1 { background-image: url('img/1.svg'); background-color: #00d; }
#b2 { background-image: url('img/2.svg'); background-color: #e00; }
#b3 { background-image: url('img/3.svg'); background-color: #dd0; }
#b4 { background-image: url('img/4.svg'); background-color: #0d0; }
#b5 { background-image: url('img/5.svg'); }
#b6 { background-image: url('img/6.svg'); }
#b7 { background-image: url('img/7.svg'); }
Expand All @@ -127,9 +140,27 @@ div {
#b14 { background-image: url('img/14.svg'); }
#b15 { background-image: url('img/15.svg'); }
#b16 { background-image: url('img/16.svg'); }

#bg { background-image: url('img/g.svg'); background-color: #444; }
#bs { background-image: url('img/s.svg'); background-color: #444; }
#bm { background-image: url('img/m.svg'); background-color: #444; }
/* printf "du\ndl\ndd\ndr" | xargs -I xx echo "#xx { background-image: url('img/xx.svg'); }" */
#du { background-image: url('img/du.svg'); }
#dl { background-image: url('img/dl.svg'); }
#dd { background-image: url('img/dd.svg'); }
#dr { background-image: url('img/dr.svg'); }

/* Analog buttons */
#a1 {background-color: #66f;}
#a2 {background-color: #f33;}
#a3 {background-color: #ff2;}
#a4 {background-color: #2f2;}

/* Motion controls */
.motion {background-color: #ddd;}

/* Pedals */
.pedal {background-color: #444;}

/* Knobs */
.knob { }
.knobcircle {background-color: #888;}
83 changes: 37 additions & 46 deletions yoke/assets/joypad/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,16 +201,12 @@ function mnemonics(a, b) {
function truncate(f, id, pattern) {
var truncated = false;
f = f.map(function(val) {
if (val < 0) { truncated = true; return 0; }
else if (val > 1) { truncated = true; return 1; }
if (val < 0.000001) { truncated = true; return 0.000001; }
else if (val > 0.999999) { truncated = true; return 0.999999; }
else { return val; }
});
if (VIBRATE_ON_PAD_BOUNDARY && pattern) {
if (truncated) {
queueForVibration(id, pattern);
} else {
unqueueForVibration(id);
}
truncated ? queueForVibration(id, pattern) : unqueueForVibration(id);
}
return f;
}
Expand Down Expand Up @@ -252,25 +248,30 @@ function Control(type, id, updateStateCallback) {
this._state = 0;
this.kernelEvent = '';
}
Control.prototype.getBoundingClientRect = function() {
this._offset = this.element.getBoundingClientRect();
this._offset.semiwidth = this._offset.width / 2;
this._offset.semiheight = this._offset.height / 2;
this._offset.xCenter = this._offset.x + this._offset.semiwidth;
this._offset.yCenter = this._offset.y + this._offset.semiheight;
};
Control.prototype.onAttached = function() {};
Control.prototype.state = function() {
return this._state.toString();
return Math.floor(256 * this._state);
};

function Joystick(id, updateStateCallback) {
Control.call(this, 'joystick', id, updateStateCallback);
this._state = [0.5, 0.5];
this.quadrant = -2;
this._locking = (id[0] == 's');
this._offset = {};
this._circle = document.createElement('div');
this._circle.className = 'circle';
this.element.appendChild(this._circle);
axes += 2;
}
Joystick.prototype = Object.create(Control.prototype);
Joystick.prototype.onAttached = function() {
this._offset = this.element.getBoundingClientRect();
this._updateCircle();
this.element.addEventListener('touchmove', this.onTouch.bind(this), false);
this.element.addEventListener('touchstart', this.onTouchStart.bind(this), false);
Expand Down Expand Up @@ -311,7 +312,12 @@ Joystick.prototype.onTouchEnd = function() {
window.navigator.vibrate(VIBRATION_MILLISECONDS_OUT);
};
Joystick.prototype._updateCircle = function() {
this._circle.style.transform = 'translate(-50%, -50%) translate(' + (this._offset.x + this._offset.width * this._state[0]) + 'px, ' + (this._offset.y + this._offset.height * this._state[1]) + 'px)';
this._circle.style.transform = 'translate(-50%, -50%) translate(' +
(this._offset.x + this._offset.width * this._state[0]) + 'px, ' +
(this._offset.y + this._offset.height * this._state[1]) + 'px)';
};
Joystick.prototype.state = function() {
return this._state.map(function(val) {return Math.floor(256 * val);});
};

function Motion(id, updateStateCallback) {
Expand Down Expand Up @@ -347,8 +353,8 @@ function Motion(id, updateStateCallback) {
Motion.prototype = Object.create(Control.prototype);
Motion.prototype._normalize = function(f) {
f *= ACCELERATION_CONSTANT;
if (f < -0.5) { f = -0.5; }
if (f > 0.5) { f = 0.5; }
if (f < -0.499999) { f = -0.499999; }
if (f > 0.499999) { f = 0.499999; }
return f + 0.5;
};
Motion.prototype.onAttached = function() {};
Expand All @@ -365,18 +371,16 @@ Motion.prototype.onDeviceOrientation = function(ev) {
motionSensor.updateStateCallback();
};
Motion.prototype.state = function() {
return motionState[this._mask].toString();
return Math.floor(256 * motionState[this._mask]);
};

function Pedal(id, updateStateCallback) {
Control.call(this, 'button', id, updateStateCallback);
this._state = 0;
this._offset = {};
axes += 1;
}
Pedal.prototype = Object.create(Control.prototype);
Pedal.prototype.onAttached = function() {
this._offset = this.element.getBoundingClientRect();
this.element.addEventListener('touchstart', this.onTouchStart.bind(this), false);
this.element.addEventListener('touchmove', this.onTouchMove.bind(this), false);
this.element.addEventListener('touchend', this.onTouchEnd.bind(this), false);
Expand Down Expand Up @@ -415,30 +419,22 @@ function AnalogButton(id, updateStateCallback) {
this.onTouchMoveParticular = function() {};
Control.call(this, 'button', id, updateStateCallback);
this._state = 0;
this._offset = {};
axes += 1;
}
AnalogButton.prototype = Object.create(Control.prototype);
AnalogButton.prototype.onAttached = function() {
this._offset = this.element.getBoundingClientRect();
this.element.addEventListener('touchstart', this.onTouchStart.bind(this), false);
this.element.addEventListener('touchmove', this.onTouchMove.bind(this), false);
this.element.addEventListener('touchend', this.onTouchEnd.bind(this), false);
if (this._offset.width > this._offset.height) {
this._offset.width /= 2;
this._offset.x += this._offset.width;
this.onTouchMoveParticular = function(ev) {
var pos = ev.targetTouches[0];
return truncate([1 - Math.abs(this._offset.x - pos.pageX) / this._offset.width]);

return truncate([1 - Math.abs(this._offset.xCenter - pos.pageX) / this._offset.semiwidth]);
};
} else {
this._offset.height /= 2;
this._offset.y += this._offset.height;
this.onTouchMoveParticular = function(ev) {
var pos = ev.targetTouches[0];
return truncate([1 - Math.abs(this._offset.y - pos.pageY) / this._offset.height]);

return truncate([1 - Math.abs(this._offset.yCenter - pos.pageY) / this._offset.semiheight]);
};
}
};
Expand Down Expand Up @@ -470,7 +466,6 @@ AnalogButton.prototype.onTouchEnd = function() {
function Knob(id, updateStateCallback) {
Control.call(this, 'knob', id, updateStateCallback);
this._state = 0;
this._offset = {};
this._knobcircle = document.createElement('div');
this._knobcircle.className = 'knobcircle';
this.element.appendChild(this._knobcircle);
Expand All @@ -481,23 +476,20 @@ function Knob(id, updateStateCallback) {
}
Knob.prototype = Object.create(Control.prototype);
Knob.prototype.onAttached = function() {
// First approximation to the knob coordinates.
this._offset = this.element.getBoundingClientRect();
// Centering the knob within the boundary.
var minDimension = Math.min(this._offset.width, this._offset.height);
if (minDimension == this._offset.width) {
this._knobcircle.style.top = this._offset.y + (this._offset.height - this._offset.width) / 2 + 'px';
if (this._offset.width < this._offset.height) {
this._offset.y += this._offset.semiheight - this._offset.semiwidth;
this._offset.height = this._offset.width;
this._offset.semiheight = this._offset.semiwidth;
} else {
this._knobcircle.style.left = this._offset.x + (this._offset.width - this._offset.height) / 2 + 'px';
this._offset.x += this._offset.semiwidth - this._offset.semiheight;
this._offset.width = this._offset.height;
this._offset.semiwidth = this._offset.semiheight;
}
this._knobcircle.style.top = this._offset.y + 'px';
this._knobcircle.style.left = this._offset.x + 'px';
this._knobcircle.style.height = this._offset.width + 'px';
this._knobcircle.style.width = this._offset.height + 'px';
// Calculating the exact center.
this._offset = this._knobcircle.getBoundingClientRect();
this._offset.x += this._offset.width / 2;
this._offset.y += this._offset.height / 2;
this._updateCircles();
this.quadrant = 0;
this.element.addEventListener('touchmove', this.onTouch.bind(this), false);
Expand All @@ -506,7 +498,7 @@ Knob.prototype.onAttached = function() {
};
Knob.prototype.onTouch = function(ev) {
var pos = ev.targetTouches[0];
this._state = Math.atan2(pos.pageY - this._offset.y, pos.pageX - this._offset.x) / 2 / Math.PI + 0.5;
this._state = Math.atan2(pos.pageY - this._offset.yCenter, pos.pageX - this._offset.xCenter) / 2 / Math.PI + 0.5;
this.updateStateCallback();
var currentQuadrant = Math.floor(this._state * 16);
if (VIBRATE_ON_QUADRANT_BOUNDARY && this.quadrant != currentQuadrant) {
Expand All @@ -531,7 +523,7 @@ Knob.prototype._updateCircles = function() {

function Button(id, updateStateCallback) {
Control.call(this, 'button', id, updateStateCallback);
this._state = 0;
this._state = false;
buttons += 1;
}
Button.prototype = Object.create(Control.prototype);
Expand All @@ -541,17 +533,20 @@ Button.prototype.onAttached = function() {
};
Button.prototype.onTouchStart = function(ev) {
ev.preventDefault(); // Android Webview delays the vibration without this.
this._state = 1;
this._state = true;
this.updateStateCallback();
this.element.classList.add('pressed');
window.navigator.vibrate(VIBRATION_MILLISECONDS_IN);
};
Button.prototype.onTouchEnd = function() {
this._state = 0;
this._state = false;
this.updateStateCallback();
this.element.classList.remove('pressed');
window.navigator.vibrate(VIBRATION_MILLISECONDS_OUT);
};
Button.prototype.state = function() {
return (this._state ? 1 : 0);
};

function Dummy(id, updateStateCallback) {
Control.call(this, 'dummy', 'dum', updateStateCallback);
Expand Down Expand Up @@ -583,6 +578,7 @@ function Joypad() {
}, this);
this._controls.forEach(function(control) {
this.element.appendChild(control.element);
control.getBoundingClientRect();
control.onAttached();
}, this);
if (axes == 0 && buttons == 0) {
Expand Down Expand Up @@ -614,11 +610,6 @@ function Joypad() {
Joypad.prototype.updateState = function() {
var state = this._controls.map(function(control) { return control.state(); }).join(',');

// We are reducing float precision to avoid getting UDP messages cut in half.
// We are re-splitting the string since some control.state() above return strings
// (e.g. because [0.5, 0.5].toString() == '0.5,0.5')
state = state.split(',').map(function(x) { return x.substr(0, 6); }).join(',');

// Within the Yoke webview, sends the joypad state.
// Outside the Yoke webview, window.Yoke.update_vals() is redefined to have no effect.
// This prevents JavaScript exceptions, and wastes less CPU time when in Yoke:
Expand Down
26 changes: 2 additions & 24 deletions yoke/assets/joypad/gamepad.css
Original file line number Diff line number Diff line change
Expand Up @@ -32,34 +32,12 @@
* g for SELECT button,
* m for the branded button (HOME or equivalent),
* 1, 2, 3, 4, 5, 6, 7, 8, 9, 10... for the rest.
* d_ for D-PAD, where _ is one of these letters:
* a_ for analog button/trigger, where _ is a number;
* d_ for D-pad, where _ is one of these letters:
* u, for the key UP,
* d, for the key DOWN,
* l, for the key LEFT,
* r, for the key RIGHT;
* dbg for debug messages;
* a period for empty space. */
}

.joystick { background-image: url("img/joystick.svg"); background-color: #bbb; }
.circle {
background-color: black;
width: 10px;
height: 10px;
border-radius: 100%;
}

.motion {background-color: #ddd;}

.pedal {background-color: #444;}

.knob { }
.knobcircle {background-color: #888;}

.button { background-color: #bbb; }
.pressed { filter: brightness(70%); }
#b1 {background-color: #22e;}
#b2 {background-color: #e00;}
#b3 {background-color: #dd0;}
#b4 {background-color: #0d0;}
#bg, #bs, #bm {background-color: #444;}
4 changes: 4 additions & 0 deletions yoke/assets/joypad/img/g.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions yoke/assets/joypad/img/m.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions yoke/assets/joypad/img/s.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 2 additions & 24 deletions yoke/assets/joypad/racing.css
Original file line number Diff line number Diff line change
Expand Up @@ -32,34 +32,12 @@
* g for SELECT button,
* m for the branded button (HOME or equivalent),
* 1, 2, 3, 4, 5, 6, 7, 8, 9, 10... for the rest.
* d_ for D-PAD, where _ is one of these letters:
* a_ for analog button/trigger, where _ is a number;
* d_ for D-pad, where _ is one of these letters:
* u, for the key UP,
* d, for the key DOWN,
* l, for the key LEFT,
* r, for the key RIGHT;
* dbg for debug messages;
* a period for empty space. */
}

.joystick { background-image: url("img/joystick.svg"); background-color: #bbb; }
.circle {
background-color: black;
width: 10px;
height: 10px;
border-radius: 100%;
}

.motion {background-color: #ddd;}

.pedal {background-color: #444;}

.knob { }
.knobcircle {background-color: #888;}

.button { background-color: #bbb; }
.pressed { filter: brightness(70%); }
#b1 {background-color: #22e;}
#b2 {background-color: #e00;}
#b3 {background-color: #dd0;}
#b4 {background-color: #0d0;}
#bg, #bs, #bm {background-color: #444;}
Loading