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
kblight.cpp
Go to the documentation of this file.
1 #include <cmath>
2 #include <QDateTime>
3 #include <QSet>
4 #include "kblight.h"
5 #include "kbmode.h"
6 
7 static int _shareDimming = -1;
8 static QSet<KbLight*> activeLights;
9 
10 KbLight::KbLight(KbMode* parent, const KeyMap& keyMap) :
11  QObject(parent), _previewAnim(0), lastFrameSignal(0), _dimming(0), _lastFrameDimming(0), _start(false), _needsSave(true), _needsMapRefresh(true)
12 {
13  map(keyMap);
14 }
15 
16 KbLight::KbLight(KbMode* parent, const KeyMap& keyMap, const KbLight& other) :
17  QObject(parent), _previewAnim(0), _map(other._map), _qColorMap(other._qColorMap), lastFrameSignal(0), _dimming(other._dimming), _lastFrameDimming(other._lastFrameDimming), _start(false), _needsSave(true), _needsMapRefresh(true)
18 {
19  map(keyMap);
20  // Duplicate animations
21  foreach(KbAnim* animation, other._animList)
22  _animList.append(new KbAnim(this, keyMap, *animation));
23 }
24 
25 void KbLight::map(const KeyMap& map){
26  // If any of the keys are missing from the color map, set them to white
27  QHashIterator<QString, Key> i(map);
28  while(i.hasNext()){
29  i.next();
30  const QString& key = i.key();
31  if(!_qColorMap.contains(key))
32  _qColorMap[key] = 0xFFFFFFFF;
33  }
34  // Set the new map
35  _map = map;
36  foreach(KbAnim* anim, _animList)
37  anim->map(map);
42  emit updated();
43 }
44 
46  activeLights.remove(this);
47 }
48 
49 void KbLight::color(const QString& key, const QColor& newColor){
50  QRgb newRgb = newColor.rgb();
51  _qColorMap[key] = newRgb;
52  _needsSave = true;
53  if(!_needsMapRefresh){
54  // Update flat map if we're not scheduled to rebuild it
55  QByteArray rawName = key.toLatin1();
56  QRgb* rawRgb = _colorMap.colorForName(rawName.data());
57  if(rawRgb)
58  *rawRgb = newRgb;
59  }
60 }
61 
62 void KbLight::color(const QColor& newColor){
63  QRgb newRgb = newColor.rgb();
65  while(i.hasNext()){
66  i.next();
67  i.value() = newRgb;
68  }
69  _needsSave = true;
70  // Reset flat map
71  _needsMapRefresh = false;
72  int mapCount = _colorMap.count();
73  QRgb* flat = _colorMap.colors();
74  for(int i = 0; i < mapCount; i++)
75  flat[i] = mapCount;
76 }
77 
79  return _shareDimming;
80 }
81 
82 void KbLight::shareDimming(int newShareDimming){
83  if(_shareDimming == newShareDimming)
84  return;
85  _shareDimming = newShareDimming;
86  if(newShareDimming != -1){
87  foreach(KbLight* light, activeLights)
88  light->dimming(newShareDimming);
89  }
90 }
91 
92 void KbLight::dimming(int newDimming){
93  if(_shareDimming != -1)
94  shareDimming(newDimming);
95  _needsSave = true;
96  _dimming = newDimming;
97  emit updated();
98 }
99 
100 KbAnim* KbLight::addAnim(const AnimScript *base, const QStringList &keys, const QString& name, const QMap<QString, QVariant>& preset){
101  // Stop and restart all existing animations
102  stopPreview();
103  quint64 timestamp = QDateTime::currentMSecsSinceEpoch();
104  foreach(KbAnim* anim, _animList){
105  anim->stop();
106  anim->trigger(timestamp);
107  }
108  // Load the new animation and set preset parameters
109  KbAnim* anim = new KbAnim(this, _map, name, keys, base);
110  QMapIterator<QString, QVariant> i(preset);
111  while(i.hasNext()){
112  i.next();
113  anim->parameter(i.key(), i.value());
114  }
115  anim->commitParams();
116  // Add the animation and start it
117  _animList.append(anim);
118  anim->trigger(timestamp);
119  _start = true;
120  _needsSave = true;
121  return anim;
122 }
123 
124 void KbLight::previewAnim(const AnimScript* base, const QStringList& keys, const QMap<QString, QVariant>& preset){
125  if(_previewAnim)
126  stopPreview();
127  quint64 timestamp = QDateTime::currentMSecsSinceEpoch();
128  // Load the new animation and set preset parameters
129  KbAnim* anim = new KbAnim(this, _map, "", keys, base);
130  QMapIterator<QString, QVariant> i(preset);
131  while(i.hasNext()){
132  i.next();
133  anim->parameter(i.key(), i.value());
134  }
135  anim->commitParams();
136  anim->reInit();
137  // Add the animation and start it
138  _previewAnim = anim;
139  anim->trigger(timestamp);
140  _start = true;
141 }
142 
144  delete _previewAnim;
145  _previewAnim = 0;
146 }
147 
149  // Stop and restart all existing animations
150  quint64 timestamp = QDateTime::currentMSecsSinceEpoch();
151  foreach(KbAnim* anim, _animList){
152  anim->stop();
153  anim->trigger(timestamp);
154  }
155  // Same as addAnim, just duplicate the existing one
156  KbAnim* anim = new KbAnim(this, _map, *oldAnim);
157  anim->newId();
158  int index = _animList.indexOf(oldAnim);
159  if(index < 0)
160  _animList.append(anim);
161  else
162  _animList.insert(index + 1, anim);
163  anim->trigger(timestamp);
164  _start = true;
165  _needsSave = true;
166  return anim;
167 }
168 
170  if(!_start)
171  return false;
172  foreach(KbAnim* animation, _animList){
173  if(!animation->isRunning())
174  return false;
175  }
176  return true;
177 }
178 
180  quint64 timestamp = QDateTime::currentMSecsSinceEpoch();
181  foreach(KbAnim* anim, _animList){
182  anim->stop();
183  anim->trigger(timestamp);
184  }
185  stopPreview();
186  _start = true;
187 }
188 
189 void KbLight::animKeypress(const QString& key, bool down){
190  foreach(KbAnim* anim, _animList){
191  if(anim->keys().contains(key))
192  anim->keypress(key, down, QDateTime::currentMSecsSinceEpoch());
193  }
194  if(_previewAnim){
195  if(_previewAnim->keys().contains(key))
196  _previewAnim->keypress(key, down, QDateTime::currentMSecsSinceEpoch());
197  }
198 }
199 
201  // Apply shared dimming if needed
202  if(_shareDimming != -1 && _shareDimming != _dimming)
204  activeLights.insert(this);
205  if(_start)
206  return;
207  quint64 timestamp = QDateTime::currentMSecsSinceEpoch();
208  foreach(KbAnim* anim, _animList)
209  anim->trigger(timestamp);
210  if(_previewAnim)
211  _previewAnim->trigger(timestamp);
212  _start = true;
213 }
214 
216  activeLights.remove(this);
217  foreach(KbAnim* anim, _animList)
218  anim->stop();
219  stopPreview();
220  _start = false;
221 }
222 
223 void KbLight::printRGB(QFile& cmd, const ColorMap &animMap){
224  int count = animMap.count();
225  const char* const* names = animMap.keyNames();
226  const QRgb* colors = animMap.colors();
227  // Print each color and the corresponding RGB value
228  for(int i = 0; i < count; i++){
229  cmd.write(" ");
230  cmd.write(names[i]);
231  char output[8];
232  QRgb color = colors[i];
233  snprintf(output, sizeof(output), ":%02x%02x%02x", qRed(color), qGreen(color), qBlue(color));
234  cmd.write(output);
235  }
236 }
237 
239  if(!_needsMapRefresh)
240  return;
241  _needsMapRefresh = false;
242  // Copy RGB values from QColorMap to ColorMap
244  while(i.hasNext()){
245  i.next();
246  QByteArray rawName = i.key().toLatin1();
247  QRgb color = i.value();
248  QRgb* rawColor = _colorMap.colorForName(rawName.data());
249  if(rawColor)
250  *rawColor = color;
251  }
252 }
253 
256  _indicatorList.clear();
257 }
258 
259 void KbLight::setIndicator(const char* name, QRgb argb){
260  QRgb* dest = _indicatorMap.colorForName(name);
261  if(dest){
262  *dest = argb;
263  _indicatorList.insert(name);
264  }
265 }
266 
267 // Colorspace conversion: linear <-> sRGB
268 // (sRGB: [0, 255], linear: [0, 1])
269 
270 static float sToL(float srgb){
271  srgb /= 255.f;
272  if(srgb <= 0.04045f)
273  return srgb / 12.92f;
274  return pow((srgb + 0.055f) / 1.055f, 2.4f);
275 }
276 
277 static float lToS(float linear){
278  if(linear <= 0.0031308f)
279  return 12.92f * linear * 255.f;
280  return (1.055f * pow(linear, 1.f / 2.4f) - 0.055f) * 255.f;
281 }
282 
283 // Convert RGB to monochrome
284 QRgb monoRgb(float r, float g, float b){
285  // It's important to use a linear colorspace for this, otherwise the colors will appear inconsistent
286  // Note that although we could use linear space for alpha blending or the animation blending functions, we don't.
287  // The reason for this is that photo manipulation programs don't do it either, so even though the result would technically be more correct,
288  // it would look wrong to most people.
289  r = sToL(r);
290  g = sToL(g);
291  b = sToL(b);
292  int value = round(lToS((r + g + b) / 3.f));
293  return qRgb(value, value, value);
294 }
295 
296 void KbLight::frameUpdate(QFile& cmd, bool monochrome){
297  rebuildBaseMap();
299  // Advance animations
300  quint64 timestamp = QDateTime::currentMSecsSinceEpoch();
301  foreach(KbAnim* anim, _animList)
302  anim->blend(_animMap, timestamp);
303  if(_previewAnim)
304  _previewAnim->blend(_animMap, timestamp);
305 
306  // Avoid expensive processing if nothing has changed from the last frame.
308  return;
312 
313  int count = _animMap.count();
314  QRgb* colors = _animMap.colors();
315  // Apply active indicators and/or perform monochrome conversion
316  if(monochrome || !_indicatorList.isEmpty()){
317  QRgb* indicators = _indicatorMap.colors();
318  for(int i = 0; i < count; i++){
319  QRgb& rgb = colors[i];
320  float r = qRed(rgb);
321  float g = qGreen(rgb);
322  float b = qBlue(rgb);
323  // Apply indicators
324  QRgb rgb2 = indicators[i];
325  if(qAlpha(rgb2) != 0){
326  float r2 = qRed(rgb2);
327  float g2 = qGreen(rgb2);
328  float b2 = qBlue(rgb2);
329  float a2 = qAlpha(rgb2) / 255.f;
330  r = round(r2 * a2 + r * (1.f - a2));
331  g = round(g2 * a2 + g * (1.f - a2));
332  b = round(b2 * a2 + b * (1.f - a2));
333  }
334  // If monochrome mode is active, average the channels to get a grayscale image
335  if(monochrome)
336  rgb = monoRgb(r, g, b);
337  else
338  rgb = qRgb(r, g, b);
339  }
340  }
341 
342  // Emit signals for the animation (only do this every 50ms - it can cause a lot of CPU usage)
343  if(timestamp >= lastFrameSignal + 50){
345  lastFrameSignal = timestamp;
346  }
347 
348  // If brightness is at 0%, turn off lighting entirely
349  if(_dimming == 3){
350  cmd.write("rgb 000000");
351  return;
352  }
353 
354  float light = (3 - _dimming) / 3.f;
355  // Apply global dimming
356  if(light != 1.f || monochrome){
357  for(int i = 0; i < count; i++){
358  QRgb& rgb = colors[i];
359  // Like the monochrome conversion, this should be done in a linear colorspace
360  float r = sToL(qRed(rgb));
361  float g = sToL(qGreen(rgb));
362  float b = sToL(qBlue(rgb));
363  r *= light;
364  g *= light;
365  b *= light;
366  r = round(lToS(r));
367  g = round(lToS(g));
368  b = round(lToS(b));
369  rgb = qRgb(r, g, b);
370  }
371  }
372 
373  // Apply light
374  cmd.write("rgb");
375  printRGB(cmd, _animMap);
376 }
377 
378 void KbLight::base(QFile &cmd, bool ignoreDim, bool monochrome){
379  close();
380  if(_dimming == MAX_DIM && !ignoreDim){
381  cmd.write(QString().sprintf("rgb 000000").toLatin1());
382  return;
383  }
384  // Set just the background color, ignoring any animation
385  rebuildBaseMap();
387  // If monochrome is active, create grayscale
388  if(monochrome){
389  int count = _animMap.count();
390  QRgb* colors = _animMap.colors();
391  for(int i = 0; i < count; i++){
392  QRgb& rgb = colors[i];
393  rgb = monoRgb(qRed(rgb), qGreen(rgb), qBlue(rgb));
394  }
395  }
396  // Set a few indicators to black as the hardware handles them differently
397  QRgb* mr = _animMap.colorForName("mr"), *m1 = _animMap.colorForName("m1"), *m2 = _animMap.colorForName("m2"), *m3 = _animMap.colorForName("m3"), *lock = _animMap.colorForName("lock");
398  if(mr) *mr = 0;
399  if(m1) *m1 = 0;
400  if(m2) *m2 = 0;
401  if(m3) *m3 = 0;
402  if(lock) *lock = 0;
403  // Send to driver
404  cmd.write("rgb");
405  printRGB(cmd, _animMap);
406 }
407 
408 void KbLight::load(CkbSettings& settings){
409  // Load light settings
410  _needsSave = false;
411  SGroup group(settings, "Lighting");
412  KeyMap currentMap = _map;
413  _map = KeyMap::fromName(settings.value("KeyMap").toString());
414  _dimming = settings.value("Brightness").toUInt();
415  if(_dimming > MAX_DIM)
416  _dimming = MAX_DIM;
417  // Load RGB settings
418  bool useReal = settings.value("UseRealNames").toBool();
419  {
420  SGroup group(settings, "Keys");
421  foreach(QString key, settings.childKeys()){
422  QString name = key.toLower();
423  if(!useReal)
424  name = _map.fromStorage(name);
425  QColor color = settings.value(key).toString();
426  if(!color.isValid())
427  color = QColor(255, 255, 255);
428  _qColorMap[name] = color.rgb();
429  }
430  _needsMapRefresh = true;
431  }
432  // Load animations
433  foreach(KbAnim* anim, _animList)
434  anim->deleteLater();
435  _animList.clear();
436  {
437  SGroup group(settings, "Animations");
438  foreach(QString anim, settings.value("List").toStringList()){
439  QUuid id = anim;
440  _animList.append(new KbAnim(this, _map, id, settings));
441  }
442  }
443  emit didLoad();
444  map(currentMap);
445 }
446 
447 void KbLight::save(CkbSettings& settings){
448  _needsSave = false;
449  SGroup group(settings, "Lighting");
450  settings.setValue("KeyMap", _map.name());
451  settings.setValue("Brightness", _dimming);
452  settings.setValue("UseRealNames", true);
453  {
454  // Save RGB settings
455  SGroup group(settings, "Keys");
457  while(i.hasNext()){
458  i.next();
459  settings.setValue(i.key(), QColor(i.value()).name());
460  }
461  }
462  {
463  // Save animations
464  SGroup group(settings, "Animations");
465  QStringList aList;
466  foreach(KbAnim* anim, _animList){
467  aList << anim->guid().toString().toUpper();
468  anim->save(settings);
469  }
470  settings.setValue("List", aList);
471  }
472 }
473 
474 bool KbLight::needsSave() const {
475  if(_needsSave)
476  return true;
477  foreach(KbAnim* anim, _animList){
478  if(anim->needsSave())
479  return true;
480  }
481  return false;
482 }
QRgb * colors()
Definition: colormap.h:33
QSet< QString > _indicatorList
Definition: kblight.h:94
void setValue(const QString &key, const QVariant &value)
void stop()
Definition: kbanim.cpp:312
static float sToL(float srgb)
Definition: kblight.cpp:270
void frameDisplayed(const ColorMap &animatedColors, const QSet< QString > &indicatorList)
void stopPreview()
Definition: kblight.cpp:143
KbAnim * duplicateAnim(KbAnim *oldAnim)
Definition: kblight.cpp:148
quint64 lastFrameSignal
Definition: kblight.h:95
KeyMap _map
Definition: kblight.h:91
KbLight(KbMode *parent, const KeyMap &keyMap)
Definition: kblight.cpp:10
QStringList childKeys() const
void init(const KeyMap &map)
Definition: colormap.cpp:69
const QStringList & keys()
Definition: kbanim.h:40
void frameUpdate(QFile &cmd, bool monochrome=false)
Definition: kblight.cpp:296
QHashIterator< QString, QRgb > QColorMapIterator
Definition: colormap.h:10
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
KbAnim * addAnim(const AnimScript *base, const QStringList &keys, const QString &name, const QMap< QString, QVariant > &preset)
Definition: kblight.cpp:100
void save(CkbSettings &settings)
Definition: kblight.cpp:447
cmd
Definition: command.h:7
bool isStarted()
Definition: kblight.cpp:169
bool needsSave() const
Definition: kblight.cpp:474
void didLoad()
AnimList _animList
Definition: kblight.h:89
void trigger(quint64 timestamp, bool ignoreParameter=false)
Definition: kbanim.cpp:209
void rebuildBaseMap()
Definition: kblight.cpp:238
ColorMap _indicatorMap
Definition: kblight.h:93
static int _shareDimming
Definition: kblight.cpp:7
static float lToS(float linear)
Definition: kblight.cpp:277
~KbLight()
Definition: kblight.cpp:45
QString fromStorage(const QString &storage)
Definition: keymap.h:151
Definition: keymap.h:49
const KeyMap & map()
Definition: kbanim.h:37
static const int MAX_DIM
Definition: kblight.h:38
void updated()
ColorMap _animMap
Definition: kblight.h:93
bool isRunning() const
Definition: kbanim.cpp:325
void newId()
Definition: kbanim.h:72
QRgb * colorForName(const char *name)
Definition: colormap.cpp:88
void save(CkbSettings &settings)
Definition: kbanim.cpp:64
void previewAnim(const AnimScript *base, const QStringList &keys, const QMap< QString, QVariant > &preset)
Definition: kblight.cpp:124
void load(CkbSettings &settings)
Definition: kblight.cpp:408
const KeyMap & map()
Definition: kblight.h:28
Definition: main.c:42
static int shareDimming()
Definition: kblight.cpp:78
void animKeypress(const QString &key, bool down)
Definition: kblight.cpp:189
QColorMap _qColorMap
Definition: kblight.h:92
Definition: kbmode.h:36
void resetIndicators()
Definition: kblight.cpp:254
void close()
Definition: kblight.cpp:215
Definition: kbanim.h:11
static QSet< KbLight * > activeLights
Definition: kblight.cpp:8
ColorMap _lastFrameIndicatorMap
Definition: kblight.h:93
const QUuid & guid() const
Definition: kbanim.h:71
void color(const QString &key, const QColor &newColor)
Definition: kblight.cpp:49
ColorMap _colorMap
Definition: kblight.h:93
bool needsSave() const
Definition: kbanim.h:29
ColorMap _lastFrameAnimMap
Definition: kblight.h:93
bool _needsSave
Definition: kblight.h:98
void reInit()
Definition: kbanim.cpp:149
void open()
Definition: kblight.cpp:200
bool _start
Definition: kblight.h:97
const char *const * keyNames() const
Definition: colormap.h:32
void setIndicator(const char *name, QRgb argb)
Definition: kblight.cpp:259
bool _needsMapRefresh
Definition: kblight.h:98
Definition: keymap.h:49
void printRGB(QFile &cmd, const ColorMap &animMap)
Definition: kblight.cpp:223
QString name() const
Definition: keymap.h:131
void restartAnimation()
Definition: kblight.cpp:179
int _lastFrameDimming
Definition: kblight.h:96
QMutableHashIterator< QString, QRgb > QMutableColorMapIterator
Definition: colormap.h:11
int count() const
Definition: colormap.h:31
static KeyMap fromName(const QString &name)
Definition: keymap.cpp:835
int dimming()
Definition: kblight.h:39
void keypress(const QString &key, bool pressed, quint64 timestamp)
Definition: kbanim.cpp:250
KbAnim * _previewAnim
Definition: kblight.h:90
QRgb monoRgb(float r, float g, float b)
Definition: kblight.cpp:284
void base(QFile &cmd, bool ignoreDim=false, bool monochrome=false)
Definition: kblight.cpp:378
void blend(ColorMap &animMap, quint64 timestamp)
Definition: kbanim.cpp:365
QVariant parameter(const QString &name) const
Definition: kbanim.h:45
int _dimming
Definition: kblight.h:96
void clear()
Definition: colormap.cpp:61
struct keyAnim * anim
Definition: main.c:55
void commitParams()
Definition: kbanim.cpp:121