ckb-next  beta-v0.2.8 at branch testing
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), _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), _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  int count = _animMap.count();
307  QRgb* colors = _animMap.colors();
308  // Apply active indicators and/or perform monochrome conversion
309  if(monochrome || !_indicatorList.isEmpty()){
310  QRgb* indicators = _indicatorMap.colors();
311  for(int i = 0; i < count; i++){
312  QRgb& rgb = colors[i];
313  float r = qRed(rgb);
314  float g = qGreen(rgb);
315  float b = qBlue(rgb);
316  // Apply indicators
317  QRgb rgb2 = indicators[i];
318  if(qAlpha(rgb2) != 0){
319  float r2 = qRed(rgb2);
320  float g2 = qGreen(rgb2);
321  float b2 = qBlue(rgb2);
322  float a2 = qAlpha(rgb2) / 255.f;
323  r = round(r2 * a2 + r * (1.f - a2));
324  g = round(g2 * a2 + g * (1.f - a2));
325  b = round(b2 * a2 + b * (1.f - a2));
326  }
327  // If monochrome mode is active, average the channels to get a grayscale image
328  if(monochrome)
329  rgb = monoRgb(r, g, b);
330  else
331  rgb = qRgb(r, g, b);
332  }
333  }
334 
335  // Emit signals for the animation (only do this every 50ms - it can cause a lot of CPU usage)
336  if(timestamp >= lastFrameSignal + 50){
338  lastFrameSignal = timestamp;
339  }
340 
341  // If brightness is at 0%, turn off lighting entirely
342  if(_dimming == 3){
343  cmd.write("rgb 000000");
344  return;
345  }
346 
347  float light = (3 - _dimming) / 3.f;
348  // Apply global dimming
349  if(light != 1.f || monochrome){
350  for(int i = 0; i < count; i++){
351  QRgb& rgb = colors[i];
352  // Like the monochrome conversion, this should be done in a linear colorspace
353  float r = sToL(qRed(rgb));
354  float g = sToL(qGreen(rgb));
355  float b = sToL(qBlue(rgb));
356  r *= light;
357  g *= light;
358  b *= light;
359  r = round(lToS(r));
360  g = round(lToS(g));
361  b = round(lToS(b));
362  rgb = qRgb(r, g, b);
363  }
364  }
365 
366  // Apply light
367  cmd.write("rgb");
368  printRGB(cmd, _animMap);
369 }
370 
371 void KbLight::base(QFile &cmd, bool ignoreDim, bool monochrome){
372  close();
373  if(_dimming == MAX_DIM && !ignoreDim){
374  cmd.write(QString().sprintf("rgb 000000").toLatin1());
375  return;
376  }
377  // Set just the background color, ignoring any animation
378  rebuildBaseMap();
380  // If monochrome is active, create grayscale
381  if(monochrome){
382  int count = _animMap.count();
383  QRgb* colors = _animMap.colors();
384  for(int i = 0; i < count; i++){
385  QRgb& rgb = colors[i];
386  rgb = monoRgb(qRed(rgb), qGreen(rgb), qBlue(rgb));
387  }
388  }
389  // Set a few indicators to black as the hardware handles them differently
390  QRgb* mr = _animMap.colorForName("mr"), *m1 = _animMap.colorForName("m1"), *m2 = _animMap.colorForName("m2"), *m3 = _animMap.colorForName("m3"), *lock = _animMap.colorForName("lock");
391  if(mr) *mr = 0;
392  if(m1) *m1 = 0;
393  if(m2) *m2 = 0;
394  if(m3) *m3 = 0;
395  if(lock) *lock = 0;
396  // Send to driver
397  cmd.write("rgb");
398  printRGB(cmd, _animMap);
399 }
400 
401 void KbLight::load(CkbSettings& settings){
402  // Load light settings
403  _needsSave = false;
404  SGroup group(settings, "Lighting");
405  KeyMap currentMap = _map;
406  _map = KeyMap::fromName(settings.value("KeyMap").toString());
407  _dimming = settings.value("Brightness").toUInt();
408  if(_dimming > MAX_DIM)
409  _dimming = MAX_DIM;
410  // Load RGB settings
411  bool useReal = settings.value("UseRealNames").toBool();
412  {
413  SGroup group(settings, "Keys");
414  foreach(QString key, settings.childKeys()){
415  QString name = key.toLower();
416  if(!useReal)
417  name = _map.fromStorage(name);
418  QColor color = settings.value(key).toString();
419  if(!color.isValid())
420  color = QColor(255, 255, 255);
421  _qColorMap[name] = color.rgb();
422  }
423  _needsMapRefresh = true;
424  }
425  // Load animations
426  foreach(KbAnim* anim, _animList)
427  anim->deleteLater();
428  _animList.clear();
429  {
430  SGroup group(settings, "Animations");
431  foreach(QString anim, settings.value("List").toStringList()){
432  QUuid id = anim;
433  _animList.append(new KbAnim(this, _map, id, settings));
434  }
435  }
436  emit didLoad();
437  map(currentMap);
438 }
439 
440 void KbLight::save(CkbSettings& settings){
441  _needsSave = false;
442  SGroup group(settings, "Lighting");
443  settings.setValue("KeyMap", _map.name());
444  settings.setValue("Brightness", _dimming);
445  settings.setValue("UseRealNames", true);
446  {
447  // Save RGB settings
448  SGroup group(settings, "Keys");
450  while(i.hasNext()){
451  i.next();
452  settings.setValue(i.key(), QColor(i.value()).name());
453  }
454  }
455  {
456  // Save animations
457  SGroup group(settings, "Animations");
458  QStringList aList;
459  foreach(KbAnim* anim, _animList){
460  aList << anim->guid().toString().toUpper();
461  anim->save(settings);
462  }
463  settings.setValue("List", aList);
464  }
465 }
466 
467 bool KbLight::needsSave() const {
468  if(_needsSave)
469  return true;
470  foreach(KbAnim* anim, _animList){
471  if(anim->needsSave())
472  return true;
473  }
474  return false;
475 }
QRgb * colors()
Definition: colormap.h:32
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:53
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:440
cmd
Definition: command.h:7
bool isStarted()
Definition: kblight.cpp:169
bool needsSave() const
Definition: kblight.cpp:467
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:135
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:72
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:401
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
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
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:31
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:115
void restartAnimation()
Definition: kblight.cpp:179
QMutableHashIterator< QString, QRgb > QMutableColorMapIterator
Definition: colormap.h:11
int count() const
Definition: colormap.h:30
static KeyMap fromName(const QString &name)
Definition: keymap.cpp:535
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:371
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:45
struct keyAnim * anim
Definition: main.c:55
void commitParams()
Definition: kbanim.cpp:121