Skip to content
Open
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
103 changes: 103 additions & 0 deletions src/main/animation/animationfire.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { RazerDeviceAnimation } from './animation';

export class RazerAnimationFire extends RazerDeviceAnimation {
constructor(device, featureConfiguration, colors = []) {
super();
this.fireEffectInterval = null;

this.device = device;
this.nRows = featureConfiguration.rows;
this.nCols = featureConfiguration.cols;
this.heat = Array(this.nRows)
.fill()
.map(() => Array(this.nCols).fill(0));

const primaryColor = colors.length >= 3 ? colors.slice(0, 3) : [0xff, 0x45, 0];
this.primaryColor = this.normalizeColor(primaryColor, [0xff, 0x45, 0]);

const secondaryColor = colors.length >= 6
? colors.slice(3, 6)
: this.blendColor(this.primaryColor, [0xff, 0xff, 0xff], 0.35);
this.secondaryColor = this.normalizeColor(secondaryColor, [0xff, 0xb0, 0]);
}

start() {
const refreshRate = 70; // milliseconds

this.fireEffectInterval = setInterval(() => {
this.heat = this.nextHeatFrame();
const matrix = this.heat.map(row => row.map(heat => this.colorForHeat(heat)));

for (let i = 0; i < this.nRows; i++) {
let row = [i, 0, this.nCols - 1, ...matrix[i].flat()];
this.device.setCustomFrame(new Uint8Array(row));
}
this.device.setModeCustom();
}, refreshRate);
}

stop() {
clearInterval(this.fireEffectInterval);
}

destroy() {
this.stop();
}

nextHeatFrame() {
return this.heat.map((row, rowIndex) => {
return row.map((heat, colIndex) => {
if (rowIndex === this.nRows - 1) {
return this.clamp01((heat * 0.35) + (Math.random() * 0.75) + 0.15);
}

const sourceRow = Math.min(this.nRows - 1, rowIndex + 1);
const sourceCol = this.clamp(colIndex + this.randomInt(-1, 1), 0, this.nCols - 1);
const belowHeat = this.heat[sourceRow][sourceCol];
const sameHeat = this.heat[rowIndex][colIndex];
const cooling = (Math.random() * 0.16) + (rowIndex / this.nRows * 0.06);

return this.clamp01((belowHeat * 0.82) + (sameHeat * 0.08) - cooling);
});
});
}

colorForHeat(heat) {
if (heat < 0.08) {
return [0, 0, 0];
}
if (heat < 0.55) {
return this.blendColor([0, 0, 0], this.primaryColor, heat / 0.55);
}
if (heat < 0.9) {
return this.blendColor(this.primaryColor, this.secondaryColor, (heat - 0.55) / 0.35);
}
return this.blendColor(this.secondaryColor, [0xff, 0xf0, 0xc0], (heat - 0.9) / 0.1);
}

blendColor(from, to, ratio) {
const clampedRatio = this.clamp01(ratio);
return from.map((channel, index) => {
return Math.round(channel + ((to[index] - channel) * clampedRatio));
});
}

normalizeColor(color, fallback) {
if (color == null || color.length < 3) {
return fallback;
}
return color.slice(0, 3).map(channel => this.clamp(Math.round(channel), 0, 255));
}

randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}

clamp01(value) {
return this.clamp(value, 0, 1);
}

clamp(value, min, max) {
return Math.min(max, Math.max(min, value));
}
}
19 changes: 17 additions & 2 deletions src/main/device/razerdevicekeyboard.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { RazerDevice } from './razerdevice';
import { RazerAnimationFire } from '../animation/animationfire';
import { RazerAnimationRipple } from '../animation/animationripple';
import { RazerAnimationWheel } from '../animation/animationwheel';

Expand All @@ -7,6 +8,7 @@ export class RazerDeviceKeyboard extends RazerDevice {
super(addon, settingsManager, stateManager, razerProperties);
this.rippleAnimation = null;
this.wheelAnimation = null;
this.fireAnimation = null;
}

async init() {
Expand All @@ -22,7 +24,7 @@ export class RazerDeviceKeyboard extends RazerDevice {
}

getSerializeIgnoredProperties() {
return super.getSerializeIgnoredProperties().concat(['rippleAnimation', 'wheelAnimation']);
return super.getSerializeIgnoredProperties().concat(['rippleAnimation', 'wheelAnimation', 'fireAnimation']);
}

getState() {
Expand All @@ -44,6 +46,9 @@ export class RazerDeviceKeyboard extends RazerDevice {
if(this.wheelAnimation != null) {
this.wheelAnimation.destroy();
}
if(this.fireAnimation != null) {
this.fireAnimation.destroy();
}
}

setModeNone() {
Expand Down Expand Up @@ -108,6 +113,9 @@ export class RazerDeviceKeyboard extends RazerDevice {
if(this.wheelAnimation != null) {
this.wheelAnimation.stop();
}
if(this.fireAnimation != null) {
this.fireAnimation.stop();
}
}

setRippleEffect(featureConfiguration, color, backgroundColor) {
Expand All @@ -124,10 +132,17 @@ export class RazerDeviceKeyboard extends RazerDevice {
this.wheelAnimation.start();
}

setFireEffect(featureConfiguration, colors) {
this.setModeState('fire', [featureConfiguration, colors]);
this.stopAnimations();
this.fireAnimation = new RazerAnimationFire(this, featureConfiguration, colors);
this.fireAnimation.start();
}

setCustomFrame(frame) {
this.addon.kbdSetCustomFrame(this.internalId, new Uint8Array(frame));
}
setModeCustom() {
this.addon.kbdSetModeCustom(this.internalId);
}
}
}
15 changes: 15 additions & 0 deletions src/main/feature/featurefire.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Feature } from './feature';
import { FeatureIdentifier } from './featureidentifier';

export class FeatureFire extends Feature {
constructor(config) {
super(FeatureIdentifier.FIRE, config);
}

getDefaultConfiguration() {
return {
rows: -1,
cols: -1,
};
}
}
5 changes: 4 additions & 1 deletion src/main/feature/featurehelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { FeatureBreathe } from './featurebreathe';
import { FeatureStarlight } from './featurestarlight';
import { FeatureRipple } from './featureripple';
import { FeatureWheel } from './featurewheel';
import { FeatureFire } from './featurefire';
import { FeatureBrightness } from './featurebrightness';
import { FeatureWaveSimple } from './featurewavesimple';
import { FeatureOldMouseEffects } from './featureoldmouseeffects';
Expand Down Expand Up @@ -34,6 +35,7 @@ export class FeatureHelper {
case FeatureIdentifier.BRIGHTNESS: return new FeatureBrightness(configuration);
case FeatureIdentifier.RIPPLE: return new FeatureRipple(configuration);
case FeatureIdentifier.WHEEL: return new FeatureWheel(configuration);
case FeatureIdentifier.FIRE: return new FeatureFire(configuration);
case FeatureIdentifier.OLD_MOUSE_EFFECTS: return new FeatureOldMouseEffects(configuration);
case FeatureIdentifier.MOUSE_BRIGHTNESS: return new FeatureMouseBrightness(configuration);
case FeatureIdentifier.POLL_RATE: return new FeatureMousePollRate(configuration);
Expand All @@ -57,6 +59,7 @@ export class FeatureHelper {
new FeatureStarlight(),
new FeatureRipple(),
new FeatureWheel(),
new FeatureFire(),
new FeatureBrightness(),
];
case RazerDeviceType.MOUSE:
Expand Down Expand Up @@ -118,4 +121,4 @@ export class FeatureHelper {
return [];
}
}
}
}
3 changes: 2 additions & 1 deletion src/main/feature/featureidentifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ FeatureIdentifier.STARLIGHT = 'starlight';
FeatureIdentifier.BRIGHTNESS = 'brightness';
FeatureIdentifier.RIPPLE = 'ripple';
FeatureIdentifier.WHEEL = 'wheel';
FeatureIdentifier.FIRE = 'fire';
FeatureIdentifier.OLD_MOUSE_EFFECTS = 'oldMouseEffects';
FeatureIdentifier.MOUSE_BRIGHTNESS = 'mouseBrightness';
FeatureIdentifier.POLL_RATE = 'pollRate';
FeatureIdentifier.MOUSE_DPI = 'dpi';
FeatureIdentifier.BATTERY = 'battery';
FeatureIdentifier.BATTERY = 'battery';
49 changes: 49 additions & 0 deletions src/main/menu/menubuilderdevice.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ function getFeatureMenuFor(application, device, feature) {
return getFeatureRipple(application, device, feature);
case FeatureIdentifier.WHEEL:
return getFeatureWheel(application, device, feature);
case FeatureIdentifier.FIRE:
return getFeatureFire(application, device, feature);
case FeatureIdentifier.OLD_MOUSE_EFFECTS:
return getFeatureOldMouseEffect(application, device, feature);
case FeatureIdentifier.MOUSE_BRIGHTNESS:
Expand Down Expand Up @@ -258,6 +260,53 @@ function getFeatureWheel(application, device, feature) {
};
}

function getFeatureFire(application, device, feature) {
const configuration = getMatrixConfiguration(device, feature);

if(configuration == null) {
return {
// device missing rows, cols config
label: 'Fire',
enabled: false
};
}

const singleItem = (label, colors) => {
return {
label: label,
click() {
device.setFireEffect(configuration, colors);
},
};
};

return {
label: 'Fire',
submenu: [
singleItem('Custom color', Object.values(device.settings.customColor1.rgb).slice(0, 3)),
singleItem('Custom dual color',
Object.values(device.settings.customColor1.rgb).slice(0, 3)
.concat(Object.values(device.settings.customColor2.rgb).slice(0, 3)),
),
singleItem('Classic', [0xff, 0x24, 0, 0xff, 0xb0, 0]),
singleItem('Blue', [0, 0x55, 0xff, 0xbf, 0xff, 0xff]),
singleItem('Green', [0, 0xff, 0x40, 0xff, 0xff, 0x80]),
],
};
}

function getMatrixConfiguration(device, feature) {
const configurations = [
feature.configuration,
device.hasFeature(FeatureIdentifier.WHEEL) ? device.getFeature(FeatureIdentifier.WHEEL).configuration : null,
device.hasFeature(FeatureIdentifier.RIPPLE) ? device.getFeature(FeatureIdentifier.RIPPLE).configuration : null,
];

return configurations.find(configuration => {
return configuration != null && configuration.rows !== -1 && configuration.cols !== -1;
});
}

function getFeatureSpectrum(application, device, feature) {
return {
label: 'Spectrum',
Expand Down
5 changes: 4 additions & 1 deletion src/main/statemanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,9 @@ export class StateManager {
case 'wheel':
device.setWheelEffect(state.args[0], state.args[1]);
break;
case 'fire':
device.setFireEffect(state.args[0], state.args[1]);
break;
default:
console.error('Unknown State mode ' + state.mode);
}
Expand All @@ -223,4 +226,4 @@ export class StateManager {
stateOnUserDidResignActive: this.stateOnUserDidResignActive,
};
}
}
}