ckb-next  v0.2.8 at branch master
ckb-next driver for corsair devices
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
kbanim.cpp
Go to the documentation of this file.
1 #include <cmath>
2 #include <QDateTime>
3 #include <QMetaEnum>
4 #include <QDebug>
5 #include "ckbsettings.h"
6 #include "kbanim.h"
7 
8 KbAnim::KbAnim(QObject *parent, const KeyMap& map, const QUuid id, CkbSettings& settings) :
9  QObject(parent), _script(0), _map(map),
10  repeatTime(0), kpRepeatTime(0), stopTime(0), kpStopTime(0), repeatMsec(0), kpRepeatMsec(0),
11  _guid(id), _isActive(false), _isActiveKp(false), _needsSave(false)
12 {
13  SGroup group(settings, _guid.toString().toUpper());
14  _keys = settings.value("Keys").toStringList();
15  // Convert key list from storage names if needed
16  if(!settings.value("UseRealNames").toBool()){
17  QMutableListIterator<QString> i(_keys);
18  while(i.hasNext()){
19  i.next();
20  QString& key = i.value();
21  key = _map.fromStorage(key);
22  }
23  }
24  _name = settings.value("Name").toString().trimmed();
25  _opacity = settings.value("Opacity").toString().toDouble();
26  if(_opacity < 0.)
27  _opacity = 0.;
28  else if(_opacity > 1.)
29  _opacity = 1.;
30  bool modeOk = false;
31  _mode = (Mode)metaObject()->enumerator(metaObject()->indexOfEnumerator("Mode")).keysToValue(settings.value("BlendMode").toString().toLatin1(), &modeOk);
32  if(!modeOk)
33  _mode = Normal;
34  _scriptName = settings.value("ScriptName").toString().trimmed();
35  _scriptGuid = settings.value("ScriptGuid").toString();
36  {
37  SGroup group(settings, "Parameters");
38  foreach(const QString& param, settings.childKeys())
39  _parameters[param.toLower()] = settings.value(param);
40  }
41 
42  if(!_scriptGuid.isNull()){
44  if(_script){
45  // Remove nonexistant parameters
46  foreach(const QString& name, _parameters.keys()){
47  AnimScript::Param param = _script->param(name);
49  _parameters.remove(name);
50  }
51  // Add defaults for unset parameters
52  QListIterator<AnimScript::Param> i = _script->paramIterator();
53  while(i.hasNext()){
54  AnimScript::Param param = i.next();
55  if(!_parameters.contains(param.name) && param.type != AnimScript::Param::LABEL)
56  _parameters[param.name] = param.defaultValue;
57  }
59  reInit();
60  }
61  }
62 }
63 
64 void KbAnim::save(CkbSettings& settings){
65  _needsSave = false;
66  settings.beginGroup(_guid.toString().toUpper());
67  settings.setValue("UseRealNames", true);
68  settings.setValue("Keys", _keys);
69  settings.setValue("Name", _name);
70  settings.setValue("Opacity", QString::number(_opacity));
71  settings.setValue("BlendMode", metaObject()->enumerator(metaObject()->indexOfEnumerator("Mode")).valueToKey(_mode));
72  settings.setValue("ScriptName", _scriptName);
73  settings.setValue("ScriptGuid", _scriptGuid.toString().toUpper());
74  settings.beginGroup("Parameters");
75  QMapIterator<QString, QVariant> i(_parameters);
76  while(i.hasNext()){
77  i.next();
78  settings.setValue(i.key(), i.value());
79  }
80  settings.endGroup();
81  settings.endGroup();
82 }
83 
84 KbAnim::KbAnim(QObject* parent, const KeyMap& map, const QString& name, const QStringList& keys, const AnimScript* script) :
85  QObject(parent),
86  _script(AnimScript::copy(this, script->guid())), _map(map), _keys(keys),
87  repeatTime(0), kpRepeatTime(0), stopTime(0), kpStopTime(0), repeatMsec(0), kpRepeatMsec(0),
88  _guid(QUuid::createUuid()), _name(name), _opacity(1.), _mode(Normal), _isActive(false), _isActiveKp(false), _needsSave(true)
89 {
90  if(_script){
91  // Set default parameters
92  QListIterator<AnimScript::Param> i = _script->paramIterator();
93  while(i.hasNext()){
94  AnimScript::Param param = i.next();
95  if(param.type != AnimScript::Param::LABEL)
96  _parameters[param.name] = param.defaultValue;
97  }
98  _scriptGuid = script->guid();
99  _scriptName = script->name();
100  reInit();
101  }
102 }
103 
104 KbAnim::KbAnim(QObject* parent, const KeyMap& map, const KbAnim& other) :
105  QObject(parent),
106  _script(AnimScript::copy(this, other.script()->guid())), _scriptGuid(_script->guid()), _scriptName(_script->name()),
107  _map(map), _keys(other._keys), _parameters(other._parameters),
108  repeatTime(0), kpRepeatTime(0), stopTime(0), kpStopTime(0), repeatMsec(0), kpRepeatMsec(0),
109  _guid(other._guid), _name(other._name), _opacity(other._opacity), _mode(other._mode), _isActive(false), _isActiveKp(false), _needsSave(true)
110 {
111  reInit();
112 }
113 
114 void KbAnim::parameter(const QString& name, const QVariant& value){
115  if(!_script->hasParam(name))
116  return;
117  _tempParameters[name] = value;
118  updateParams();
119 }
120 
122  _needsSave = true;
124  _tempParameters.clear();
125 }
126 
128  _tempParameters.clear();
129  updateParams();
130 }
131 
133  if(_script)
135  repeatKey = "";
136 }
137 
138 QMap<QString, QVariant> KbAnim::effectiveParams(){
139  QMap<QString, QVariant> res = _parameters;
140  // Apply all uncommited parameters
141  QMapIterator<QString, QVariant> i(_tempParameters);
142  while(i.hasNext()){
143  i.next();
144  res[i.key()] = i .value();
145  }
146  return res;
147 }
148 
150  if(_script)
152  repeatKey = "";
153  _isActive = _isActiveKp = false;
154 }
155 
156 void KbAnim::map(const KeyMap& newMap){
157  _map = newMap;
158  reInit();
159 }
160 
161 void KbAnim::keys(const QStringList& newKeys){
162  _keys = newKeys;
163  reInit();
164 }
165 
166 void KbAnim::catchUp(quint64 timestamp){
167  QMap<QString, QVariant> parameters = effectiveParams();
168  // Stop the animation if its time has run out
169  if(stopTime != 0 && timestamp >= stopTime){
170  repeatMsec = repeatTime = 0;
171  if(!parameters.contains("repeat")){
172  // If repeats aren't allowed, stop the animation entirely
173  _script->end();
174  _isActive = false;
175  return;
176  } else
177  // Otherwise, simply stop repeating
178  stopTime = 0;
179  }
180  if(kpStopTime != 0 && timestamp >= kpStopTime){
182  if(!parameters.contains("kprepeat")){
183  _script->end();
184  _isActiveKp = false;
185  return;
186  } else
187  kpStopTime = 0;
188  }
189 
190  // Restart (or start, if there was a delay) the animation if its repeat time is up
191  while(repeatTime > 0 && timestamp >= repeatTime){
193  if(repeatMsec <= 0){
194  repeatTime = 0;
195  break;
196  }
198  }
199  while(kpRepeatTime > 0 && timestamp >= kpRepeatTime){
201  if(kpRepeatMsec <= 0){
202  kpRepeatTime = 0;
203  break;
204  }
206  }
207 }
208 
209 void KbAnim::trigger(quint64 timestamp, bool ignoreParameter){
210  if(!_script)
211  return;
212  QMap<QString, QVariant> parameters = effectiveParams();
213 
214  catchUp(timestamp);
215  if(parameters.value("trigger").toBool() || ignoreParameter){
216  _isActive = true;
217  int delay = round(parameters.value("delay").toDouble() * 1000.);
218  if(delay > 0){
219  // If delay is enabled, wait to trigger the event
220  timestamp += delay;
221  repeatTime = timestamp;
222  } else
223  _script->retrigger(timestamp, true);
224 
225  int repeat = round(parameters.value("repeat").toDouble() * 1000.);
226  if(repeat <= 0){
227  // If no repeat allowed, calculate stop time in seconds
228  repeatMsec = -1;
229  double stop = parameters.value("stop").toDouble();
230  if(stop <= 0)
231  stopTime = 0;
232  else
233  stopTime = timestamp + round(stop * 1000.);
234  } else {
235  // If repeat is allowed, calculate stop time in repetitions
236  repeatMsec = repeat;
237  if(delay <= 0)
238  repeatTime = timestamp + repeat;
239  int stop = parameters.value("stop").toInt();
240  if(stop < 0)
241  stopTime = 0;
242  else
243  stopTime = timestamp + repeat * (1 + stop);
244  }
245  }
246  // Ask the script for a frame even if we didn't do anything here. This way ckb knows the script is responding.
247  _script->frame(timestamp);
248 }
249 
250 void KbAnim::keypress(const QString& key, bool pressed, quint64 timestamp){
251  if(!_script)
252  return;
253  QMap<QString, QVariant> parameters = effectiveParams();
254  if(pressed && parameters.value("kpmodestop").toBool()){
255  // If stop on key press is enabled, stop mode-wide animation
256  catchUp(timestamp);
257  _script->stop(timestamp);
259  _isActive = false;
260  } else {
261  if(!parameters.value("kptrigger").toBool())
262  return;
263  catchUp(timestamp);
264  }
265 
266  if(pressed){
267  // Key pressed
268  _isActiveKp = true;
269  if(parameters.value("kpmode", 0).toInt() == 2 && isActive())
270  // If mode is start once and a key has already been pressed, do nothing
271  return;
272  int delay = round(parameters.value("kpdelay").toDouble() * 1000.);
273  if(delay > 0){
274  // If delay is enabled, wait to trigger the event
275  timestamp += delay;
276  kpRepeatTime = timestamp;
277  } else
278  _script->keypress(key, pressed, timestamp);
279 
280  int repeat = round(parameters.value("kprepeat").toDouble() * 1000.);
281  if(repeat <= 0){
282  // If no repeat allowed, calculate stop time in seconds
283  kpRepeatMsec = -1;
284  double stop = parameters.value("kpstop").toDouble();
285  if(stop <= 0.)
286  kpStopTime = 0;
287  else
288  kpStopTime = timestamp + round(stop * 1000.);
289  } else {
290  // If repeat is allowed, calculate stop time in repetitions
291  kpRepeatMsec = repeat;
292  if(delay <= 0)
293  kpRepeatTime = timestamp + repeat;
294  int stop = parameters.value("kpstop").toInt();
295  if(stop < 0)
296  kpStopTime = 0;
297  else
298  kpStopTime = timestamp + repeat * (1 + stop);
299  }
300  repeatKey = key;
301  } else {
302  // Key released
303  _isActiveKp = false;
304  _script->keypress(key, pressed, timestamp);
305  if(parameters.value("kprelease").toBool())
306  // Stop repeating keypress if "Stop on key release" is enabled
307  kpStopTime = timestamp;
308  }
309  _script->frame(timestamp);
310 }
311 
313  if(_script)
314  _script->end();
315  repeatTime = 0;
316  kpRepeatTime = 0;
317  repeatMsec = 0;
318  kpRepeatMsec = 0;
319  stopTime = 0;
320  kpStopTime = 0;
321  repeatKey = "";
322  _isActive = _isActiveKp = false;
323 }
324 
325 bool KbAnim::isRunning() const {
326  if(!_script)
327  return true; // If the script wasn't loaded, pretend it's running anyway so it won't lock up
328  return _script->hasFrame();
329 }
330 
331 // Blending functions
332 
333 static float blendNormal(float bg, float fg){
334  return fg;
335 }
336 
337 static float blendAdd(float bg, float fg){
338  float res = bg + fg;
339  if(res > 1.f)
340  res = 1.f;
341  return res;
342 }
343 
344 static float blendSubtract(float bg, float fg){
345  float res = bg - fg;
346  if(res < 0.f)
347  res = 0.f;
348  return res;
349 }
350 
351 static float blendMultiply(float bg, float fg){
352  return bg * fg;
353 }
354 
355 static float blendDivide(float bg, float fg){
356  float res = bg / fg;
357  if(res > 1.f)
358  res = 1.f;
359  return res;
360 }
361 
362 typedef float (*blendFunc)(float,float);
364 
365 void KbAnim::blend(ColorMap& animMap, quint64 timestamp){
366  if(!_script)
367  return;
368 
369  // Fetch the next frame from the script
370  catchUp(timestamp);
371  _script->frame(timestamp);
372 
373  // Blend the script's map with the current map
374  int blendMode = (int)_mode;
375  blendFunc blend = functions[blendMode];
376  float fOpacity = _opacity / 255.f; // save some math by pre-dividing the 255 for qAlpha
377 
378  const ColorMap& scriptMap = _script->colors();
379  int count = animMap.count();
380  if(scriptMap.count() != count){
381  qDebug() << "Script map didn't match base map (" << count << " vs " << scriptMap.count() << "). This should never happen.";
382  return;
383  }
384  QRgb* background = animMap.colors();
385  const QRgb* foreground = scriptMap.colors();
386  for(int i = 0; i < count; i++){
387  // Mix the colors in with the color map according to blend mode and alpha
388  QRgb& bg = background[i];
389  QRgb fg = foreground[i];
390  int alpha = qAlpha(fg);
391  if(alpha == 0)
392  continue;
393  if(blendMode == 0){
394  // Blend: normal
395  // This is the most common use case and it requires much less arithmetic
396  if(alpha == 255){
397  bg = fg;
398  } else {
399  float r = qRed(bg), g = qGreen(bg), b = qBlue(bg);
400  float a = alpha * fOpacity;
401  r = r * (1.f - a) + qRed(fg) * a;
402  g = g * (1.f - a) + qGreen(fg) * a;
403  b = b * (1.f - a) + qBlue(fg) * a;
404  bg = qRgb(round(r), round(g), round(b));
405  }
406  } else {
407  // Use blend function
408  float r = qRed(bg) / 255.f, g = qGreen(bg) / 255.f, b = qBlue(bg) / 255.f;
409  float a = alpha * fOpacity;
410  r = r * (1.f - a) + blend(r, qRed(fg) / 255.f) * a;
411  g = g * (1.f - a) + blend(g, qGreen(fg) / 255.f) * a;
412  b = b * (1.f - a) + blend(b, qBlue(fg) / 255.f) * a;
413  bg = qRgb(round(r * 255.f), round(g * 255.f), round(b * 255.f));
414  }
415  }
416 }
QRgb * colors()
Definition: colormap.h:33
void setValue(const QString &key, const QVariant &value)
void stop()
Definition: kbanim.cpp:312
Param param(const QString &name) const
Definition: animscript.h:73
QStringList childKeys() const
const QStringList & keys()
Definition: kbanim.h:40
void catchUp(quint64 timestamp)
Definition: kbanim.cpp:166
bool isActive() const
Definition: kbanim.h:63
static float blendAdd(float bg, float fg)
Definition: kbanim.cpp:337
void updateParams()
Definition: kbanim.cpp:132
void retrigger(quint64 timestamp, bool allowPreempt=false)
Definition: animscript.cpp:331
quint64 repeatTime
Definition: kbanim.h:105
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
QMap< QString, QVariant > _parameters
Definition: kbanim.h:94
void endGroup()
Definition: ckbsettings.cpp:85
QVariant defaultValue
Definition: animscript.h:43
void frame(quint64 timestamp)
Definition: animscript.cpp:430
void trigger(quint64 timestamp, bool ignoreParameter=false)
Definition: kbanim.cpp:209
static float blendNormal(float bg, float fg)
Definition: kbanim.cpp:333
const QString & name() const
Definition: kbanim.h:73
static blendFunc functions[5]
Definition: kbanim.cpp:363
Mode _mode
Definition: kbanim.h:113
quint64 stopTime
Definition: kbanim.h:105
const ColorMap & colors() const
Definition: animscript.h:99
QString repeatKey
Definition: kbanim.h:104
QUuid _scriptGuid
Definition: kbanim.h:88
QString fromStorage(const QString &storage)
Definition: keymap.h:151
Definition: keymap.h:49
const KeyMap & map()
Definition: kbanim.h:37
static float blendMultiply(float bg, float fg)
Definition: kbanim.cpp:351
int repeatMsec
Definition: kbanim.h:106
bool isRunning() const
Definition: kbanim.cpp:325
int kpRepeatMsec
Definition: kbanim.h:106
void save(CkbSettings &settings)
Definition: kbanim.cpp:64
bool hasParam(const QString &name) const
Definition: animscript.h:74
static float blendDivide(float bg, float fg)
Definition: kbanim.cpp:355
bool _isActive
Definition: kbanim.h:114
QMap< QString, QVariant > effectiveParams()
Definition: kbanim.cpp:138
QString _name
Definition: kbanim.h:111
void beginGroup(const QString &prefix)
Definition: ckbsettings.cpp:81
static float blendSubtract(float bg, float fg)
Definition: kbanim.cpp:344
KbAnim(QObject *parent, const KeyMap &map, const QUuid id, CkbSettings &settings)
Definition: kbanim.cpp:8
bool _needsSave
Definition: kbanim.h:115
Definition: kbanim.h:11
float(* blendFunc)(float, float)
Definition: kbanim.cpp:362
void init(const KeyMap &map, const QStringList &keys, const QMap< QString, QVariant > &paramValues)
Definition: animscript.cpp:239
void keypress(const QString &key, bool pressed, quint64 timestamp)
Definition: animscript.cpp:354
const QString & name() const
Definition: animscript.h:60
QUuid _guid
Definition: kbanim.h:110
bool hasFrame() const
Definition: animscript.h:96
float _opacity
Definition: kbanim.h:112
void end()
Definition: animscript.cpp:387
void reInit()
Definition: kbanim.cpp:149
Definition: keymap.h:49
static AnimScript * copy(QObject *parent, const QUuid &id)
Definition: animscript.cpp:67
QListIterator< Param > paramIterator() const
Definition: animscript.h:72
QString _scriptName
Definition: kbanim.h:89
QStringList _keys
Definition: kbanim.h:92
quint64 kpRepeatTime
Definition: kbanim.h:105
AnimScript * _script
Definition: kbanim.h:86
void resetParams()
Definition: kbanim.cpp:127
int count() const
Definition: colormap.h:31
const QUuid & guid() const
Definition: animscript.h:59
quint64 kpStopTime
Definition: kbanim.h:105
KeyMap _map
Definition: kbanim.h:91
QMap< QString, QVariant > _tempParameters
Definition: kbanim.h:96
void keypress(const QString &key, bool pressed, quint64 timestamp)
Definition: kbanim.cpp:250
Mode
Definition: kbanim.h:17
void parameters(const QMap< QString, QVariant > &paramValues)
Definition: animscript.cpp:265
void blend(ColorMap &animMap, quint64 timestamp)
Definition: kbanim.cpp:365
QVariant parameter(const QString &name) const
Definition: kbanim.h:45
void stop(quint64 timestamp)
Definition: animscript.cpp:344
void commitParams()
Definition: kbanim.cpp:121
bool _isActiveKp
Definition: kbanim.h:114