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
kb.cpp
Go to the documentation of this file.
1 #include <fcntl.h>
2 #include <QSet>
3 #include <QUrl>
4 #include <QMutex>
5 #include <QDateTime>
6 #include "kb.h"
7 #include "kbmanager.h"
8 
9 // All active devices
10 static QSet<Kb*> activeDevices;
11 // Active notification node paths
12 static QSet<QString> notifyPaths;
13 static QMutex notifyPathMutex;
14 
15 int Kb::_frameRate = 30, Kb::_scrollSpeed = 0;
17 bool Kb::_dither = false, Kb::_mouseAccel = true, Kb::_delay = false;
18 
19 Kb::Kb(QObject *parent, const QString& path) :
20  QThread(parent), features("N/A"), firmware("N/A"), pollrate("N/A"), monochrome(false),
21  devpath(path), cmdpath(path + "/cmd"), notifyPath(path + "/notify1"), macroPath(path + "/notify2"),
22  _currentProfile(0), _currentMode(0), _model(KeyMap::NO_MODEL),
23  lastAutoSave(QDateTime::currentMSecsSinceEpoch()),
24  _hwProfile(0), prevProfile(0), prevMode(0),
25  cmd(cmdpath), notifyNumber(1), macroNumber(2), _needsSave(false)
26 {
27  memset(iState, 0, sizeof(iState));
28  memset(hwLoading, 0, sizeof(hwLoading));
29 
30  // Get the features, model, serial number, FW version (if available), and poll rate (if available) from /dev nodes
31  QFile ftpath(path + "/features"), mpath(path + "/model"), spath(path + "/serial"), fwpath(path + "/fwversion"), ppath(path + "/pollrate");
32  if(ftpath.open(QIODevice::ReadOnly)){
33  features = ftpath.read(1000);
34  features = features.trimmed();
35  ftpath.close();
36  // Read model from features (first word: vendor, second word: product)
37  QStringList list = features.split(" ");
38  if(list.length() < 2)
39  return;
40  _model = KeyMap::getModel(list[1]);
42  return;
43  } else
44  // Bail if features aren't readable
45  return;
46  if(features.contains("monochrome"))
47  monochrome = true;
48  if(mpath.open(QIODevice::ReadOnly)){
49  usbModel = mpath.read(100);
50  usbModel = usbModel.remove("Corsair").remove("Gaming").remove("Keyboard").remove("Mouse").remove("Bootloader").trimmed();
51  mpath.close();
52  }
53  if(usbModel == "")
54  usbModel = "Keyboard";
55  if(spath.open(QIODevice::ReadOnly)){
56  usbSerial = spath.read(100);
57  usbSerial = usbSerial.trimmed().toUpper();
58  spath.close();
59  }
60  if(usbSerial == "")
61  usbSerial = "Unknown-" + usbModel;
62  if(features.contains("fwversion") && fwpath.open(QIODevice::ReadOnly)){
63  firmware = fwpath.read(100);
64  firmware = QString::number(firmware.trimmed().toInt() / 100., 'f', 2);
65  fwpath.close();
66  }
67  if(features.contains("pollrate") && ppath.open(QIODevice::ReadOnly)){
68  pollrate = ppath.read(100);
69  pollrate = pollrate.trimmed();
70  ppath.close();
71  }
72 
73  prefsPath = "Devices/" + usbSerial;
74 
75  hwModeCount = (_model == KeyMap::K95) ? 3 : 1;
76  // Open cmd in non-blocking mode so that it doesn't lock up if nothing is reading
77  // (e.g. if the daemon crashed and didn't clean up the node)
78  int fd = open(cmdpath.toLatin1().constData(), O_WRONLY | O_NONBLOCK);
79  if(!cmd.open(fd, QIODevice::WriteOnly, QFileDevice::AutoCloseHandle))
80  return;
81 
82  // Find an available notification node (if none is found, take notify1)
83  {
84  QMutexLocker locker(&notifyPathMutex);
85  for(int i = 1; i < 10; i++){
86  QString notify = QString(path + "/notify%1").arg(i);
87  if(!QFile::exists(notify) && !notifyPaths.contains(notify)){
88  notifyNumber = i;
89  notifyPath = notify;
90  break;
91  }
92  }
93  notifyPaths.insert(notifyPath);
94  }
95  cmd.write(QString("notifyon %1\n").arg(notifyNumber).toLatin1());
96  cmd.flush();
97 
98  // Again, find an available notification node for macro definition
99  // (if none is found, take notify2)
100  {
101  QMutexLocker locker(&notifyPathMutex);
102  for(int i = 1; i < 10; i++){
103  QString notify = QString(path + "/notify%1").arg(i);
104  if(!QFile::exists(notify) && !notifyPaths.contains(notify)){
105  macroNumber = i;
106  macroPath = notify;
107  break;
108  }
109  }
110  notifyPaths.insert(notifyPath);
111  }
112  // Activate device, apply settings, and ask for hardware profile
113  cmd.write(QString("fps %1\n").arg(_frameRate).toLatin1());
114  cmd.write(QString("dither %1\n").arg(static_cast<int>(_dither)).toLatin1());
115  cmd.write(QString("\ndelay %1\n").arg(_delay? "on" : "off").toLatin1());
116 #ifdef Q_OS_MACX
117  // Write ANSI/ISO flag to daemon (OSX only)
118  cmd.write("layout ");
119  cmd.write(KeyMap::isISO(_layout) ? "iso" : "ansi");
120  // Also OSX only: scroll speed and mouse acceleration
121  cmd.write(QString("accel %1\n").arg(QString(_mouseAccel ? "on" : "off")).toLatin1());
122  cmd.write(QString("scrollspeed %1\n").arg(_scrollSpeed).toLatin1());
123 #endif
124  cmd.write(QString("\nactive\n@%1 get :hwprofileid").arg(notifyNumber).toLatin1());
125  hwLoading[0] = true;
126  for(int i = 0; i < hwModeCount; i++){
127  cmd.write(QString(" mode %1 get :hwid").arg(i + 1).toLatin1());
128  hwLoading[i + 1] = true;
129  }
130  // Ask for current indicator and key state
131  cmd.write(" get :i :keys\n");
132  cmd.flush();
133 
134  emit infoUpdated();
135  activeDevices.insert(this);
136 
137  // Start a separate thread to read from the notification node
138  start();
139 }
140 
142  // Save settings first
143  save();
144 
145  // remove the notify channel from the list of notifyPaths.
148  notifyPaths.remove(macroPath);
149 
150  // Kill notification thread and remove node
151  activeDevices.remove(this);
152  if(!isOpen()){
153  terminate();
154  wait(1000);
155  return;
156  }
157  if(notifyNumber > 0)
158  cmd.write(QString("idle\nnotifyoff %1\n").arg(notifyNumber).toLatin1());
159  cmd.flush();
160  terminate();
161  wait(1000);
162  cmd.close();
163 }
164 
165 void Kb::frameRate(int newFrameRate){
166  KbManager::fps(newFrameRate);
167  // If the rate has changed, send to all devices
168  if(newFrameRate == _frameRate)
169  return;
170  _frameRate = newFrameRate;
171  foreach(Kb* kb, activeDevices){
172  kb->cmd.write(QString("fps %1\n").arg(newFrameRate).toLatin1());
173  kb->cmd.flush();
174  }
175 }
176 
177 void Kb::layout(KeyMap::Layout newLayout){
178  if(newLayout == KeyMap::NO_LAYOUT || newLayout == _layout)
179  return;
180  _layout = newLayout;
181  // Update all devices
182  foreach(Kb* kb, activeDevices)
183  kb->updateLayout();
184 }
185 
187 #ifdef Q_OS_MACX
188  // Write ANSI/ISO flag to daemon (OSX only)
189  cmd.write("layout ");
190  cmd.write(KeyMap::isISO(_layout) ? "iso" : "ansi");
191  cmd.write("\n");
192  cmd.flush();
193 #endif
194  foreach(KbProfile* profile, _profiles)
195  profile->keyMap(getKeyMap());
196  if(_hwProfile && !_profiles.contains(_hwProfile))
198  // Stop all animations as they'll need to be restarted
199  foreach(KbMode* mode, _currentProfile->modes())
200  mode->light()->close();
201  emit infoUpdated();
202 }
203 
204 void Kb::dither(bool newDither){
205  if(newDither == _dither)
206  return;
207  _dither = newDither;
208  // Update all devices
209  foreach(Kb* kb, activeDevices){
210  kb->cmd.write(QString("dither %1\n").arg(static_cast<int>(newDither)).toLatin1());
211  kb->cmd.flush();
212  }
213 }
214 
215 void Kb::mouseAccel(bool newAccel){
216  if(newAccel == _mouseAccel)
217  return;
218  _mouseAccel = newAccel;
219 #ifdef Q_OS_MACX
220  // Update all devices
221  foreach(Kb* kb, activeDevices){
222  kb->cmd.write(QString("accel %1\n").arg(QString(newAccel ? "on" : "off")).toLatin1());
223  kb->cmd.flush();
224  }
225 #endif
226 }
227 
228 void Kb::scrollSpeed(int newSpeed){
229  if(newSpeed == _scrollSpeed)
230  return;
231  _scrollSpeed = newSpeed;
232 #ifdef Q_OS_MACX
233  // Update all devices
234  foreach(Kb* kb, activeDevices){
235  kb->cmd.write(QString("scrollspeed %1\n").arg(newSpeed).toLatin1());
236  kb->cmd.flush();
237  }
238 #endif
239 }
240 
241 void Kb::load(){
242  if(prefsPath.isEmpty())
243  return;
244  _needsSave = false;
245  CkbSettings settings(prefsPath);
246  // Read profiles
247  KbProfile* newCurrentProfile = 0;
248  QString current = settings.value("CurrentProfile").toString().trimmed().toUpper();
249  foreach(QString guid, settings.value("Profiles").toString().split(" ")){
250  guid = guid.trimmed().toUpper();
251  if(guid != ""){
252  KbProfile* profile = new KbProfile(this, getKeyMap(), settings, guid);
253  _profiles.append(profile);
254  if(guid == current || !newCurrentProfile)
255  newCurrentProfile = profile;
256  }
257  }
258  if(newCurrentProfile)
259  setCurrentProfile(newCurrentProfile);
260  else {
261  // If nothing was loaded, load the demo profile
262  QSettings demoSettings(":/txt/demoprofile.conf", QSettings::IniFormat, this);
263  CkbSettings cSettings(demoSettings);
264  KbProfile* demo = new KbProfile(this, getKeyMap(), cSettings, "{BA7FC152-2D51-4C26-A7A6-A036CC93D924}");
265  _profiles.append(demo);
266  setCurrentProfile(demo);
267  }
268 
269  emit infoUpdated();
270  emit profileAdded();
271 }
272 
273 void Kb::save(){
274  if(prefsPath.isEmpty())
275  return;
276  _needsSave = false;
277  CkbSettings settings(prefsPath, true);
278  QString guids, currentGuid;
279  foreach(KbProfile* profile, _profiles){
280  guids.append(" " + profile->id().guidString());
281  if(profile == _currentProfile)
282  currentGuid = profile->id().guidString();
283  profile->save(settings);
284  }
285  settings.setValue("CurrentProfile", currentGuid);
286  settings.setValue("Profiles", guids.trimmed());
287 }
288 
290  quint64 now = QDateTime::currentMSecsSinceEpoch();
291  if(needsSave() && now >= lastAutoSave + 15 * 1000 && !CkbSettings::isBusy()){
292  save();
293  lastAutoSave = now;
294  }
295 }
296 
297 void Kb::hwSave(){
298  if(!_currentProfile)
299  return;
300  // Close active lighting (if any)
301  if(prevMode){
302  prevMode->light()->close();
303  deletePrevious();
304  }
308  // Re-send the current profile from scratch to ensure consistency
310  // Make sure there are enough modes
312  _currentProfile->append(new KbMode(this, getKeyMap()));
313  // Write only the base colors of each mode, no animations
314  for(int i = 0; i < hwModeCount; i++){
315  KbMode* mode = _currentProfile->modes()[i];
316  cmd.write(QString("\nmode %1").arg(i + 1).toLatin1());
317  KbLight* light = mode->light();
318  KbPerf* perf = mode->perf();
319  if(mode == _currentMode)
320  cmd.write(" switch");
321  // Write the mode name and ID
322  cmd.write(" name ");
323  cmd.write(QUrl::toPercentEncoding(mode->name()));
324  cmd.write(" id ");
325  cmd.write(mode->id().guidString().toLatin1());
326  cmd.write(" ");
327  cmd.write(mode->id().modifiedString().toLatin1());
328  cmd.write(" ");
329  // Write lighting and performance
330  light->base(cmd, true, monochrome);
331  cmd.write(" ");
332  perf->update(cmd, notifyNumber, true, false);
333  // Update mode ID
334  mode->id().hwModified = mode->id().modified;
335  mode->setNeedsSave();
336  }
337  cmd.write("\n");
338 
339  // Save the profile to memory
340  cmd.write("hwsave\n");
341  cmd.flush();
342 }
343 
344 bool Kb::needsSave() const {
345  if(_needsSave)
346  return true;
347  foreach(const KbProfile* profile, _profiles){
348  if(profile->needsSave())
349  return true;
350  }
351  return false;
352 }
353 
355  cmd.write("eraseprofile");
356  // Write the profile name and ID
357  cmd.write(" profilename ");
358  cmd.write(QUrl::toPercentEncoding(_currentProfile->name()));
359  cmd.write(" profileid ");
360  cmd.write(_currentProfile->id().guidString().toLatin1());
361  cmd.write(" ");
362  cmd.write(_currentProfile->id().modifiedString().toLatin1());
363 }
364 
365 void Kb::fwUpdate(const QString& path){
366  fwUpdPath = path;
367  // Write the active command to ensure it's not ignored
368  cmd.write("active");
369  cmd.write(QString(" @%1 ").arg(notifyNumber).toLatin1());
370  cmd.write("fwupdate ");
371  cmd.write(path.toLatin1());
372  cmd.write("\n");
373 }
374 
376  // Advance animation frame
377  if(!_currentMode)
378  return;
379  KbLight* light = _currentMode->light();
380  KbBind* bind = _currentMode->bind();
381  KbPerf* perf = _currentMode->perf();
382  if(!light->isStarted()){
383  // Don't do anything until the animations are started
384  light->open();
385  return;
386  }
387 
388  // Stop animations on the previously active mode (if any)
389  bool changed = false;
390  if(prevMode != _currentMode){
391  if(prevMode){
392  prevMode->light()->close();
393  disconnect(prevMode, SIGNAL(destroyed()), this, SLOT(deletePrevious()));
394  }
396  connect(prevMode, SIGNAL(destroyed()), this, SLOT(deletePrevious()));
397  changed = true;
398  }
399 
400  // If the profile has changed, update it
403  cmd.write(" ");
405  }
406 
407  // Update current mode
408  int index = _currentProfile->indexOf(_currentMode);
409  // ckb-daemon only has 6 modes: 3 hardware, 3 non-hardware. Beyond mode six, switch back to four.
410  // e.g. 1, 2, 3, 4, 5, 6, 4, 5, 6, 4, 5, 6 ...
411  if(index >= 6)
412  index = 3 + index % 3;
413 
414  // Send lighting/binding to driver
415  cmd.write(QString("mode %1 switch ").arg(index + 1).toLatin1());
416  perf->applyIndicators(index, iState);
417  light->frameUpdate(cmd, monochrome);
418  cmd.write(QString("\n@%1 ").arg(notifyNumber).toLatin1());
419  bind->update(cmd, changed);
420  cmd.write(" ");
421  perf->update(cmd, notifyNumber, changed, true);
422  cmd.write("\n");
423  cmd.flush();
424 }
425 
427  disconnect(prevMode, SIGNAL(destroyed()), this, SLOT(deletePrevious()));
428  prevMode = 0;
429 }
430 
431 void Kb::hwProfile(KbProfile* newHwProfile){
432  if(_hwProfile == newHwProfile)
433  return;
434  if(_hwProfile)
435  disconnect(_hwProfile, SIGNAL(destroyed()), this, SLOT(deleteHw()));
436  _hwProfile = newHwProfile;
437  if(_hwProfile)
438  connect(_hwProfile, SIGNAL(destroyed()), this, SLOT(deleteHw()));
439 }
440 
442  disconnect(_hwProfile, SIGNAL(destroyed()), this, SLOT(deleteHw()));
443  _hwProfile = 0;
444 }
445 
446 void Kb::run(){
447  QFile notify(notifyPath);
448  // Wait a small amount of time for the node to open (100ms)
449  QThread::usleep(100000);
450  if(!notify.open(QIODevice::ReadOnly)){
451  // If it's still not open, try again before giving up (1s at a time, 10s total)
452  QThread::usleep(900000);
453  for(int i = 1; i < 10; i++){
454  if(notify.open(QIODevice::ReadOnly))
455  break;
456  QThread::sleep(1);
457  }
458  if(!notify.isOpen())
459  return;
460  }
461  // Read data from notification node
462  QByteArray line;
463  while(notify.isOpen() && (line = notify.readLine()).length() > 0){
464  QString text = QString::fromUtf8(line);
465  metaObject()->invokeMethod(this, "readNotify", Qt::QueuedConnection, Q_ARG(QString, text));
466  }
467  QMutexLocker locker(&notifyPathMutex);
468  notifyPaths.remove(notifyPath);
469 }
470 
471 void Kb::readNotify(QString line){
472  QStringList components = line.trimmed().split(" ");
473  if(components.count() < 2)
474  return;
475  if(components[0] == "key"){
476  // Key event
477  QString key = components[1];
478  if(key.length() < 2)
479  return;
480  QString keyName = key.mid(1);
481  bool keyPressed = (key[0] == '+');
482  KbMode* mode = _currentMode;
483  if(mode){
484  mode->light()->animKeypress(keyName, keyPressed);
485  mode->bind()->keyEvent(keyName, keyPressed);
486  }
487  } else if(components[0] == "i"){
488  // Indicator event
489  QString i = components[1];
490  if(i.length() < 2)
491  return;
492  QString iName = i.mid(1);
493  bool on = (i[0] == '+');
494  if(iName == "num")
495  iState[0] = on;
496  else if(iName == "caps")
497  iState[1] = on;
498  else if(iName == "scroll")
499  iState[2] = on;
500  } else if(components[0] == "hwprofileid"){
501  // Hardware profile ID
502  if(components.count() < 3)
503  return;
504  // Find the hardware profile in the list of profiles
505  QString guid = components[1];
506  QString modified = components[2];
507  KbProfile* newProfile = 0;
508  foreach(KbProfile* profile, _profiles){
509  if(profile->id().guid == guid){
510  newProfile = profile;
511  break;
512  }
513  }
514  // If it wasn't found, create it
515  if(!newProfile){
516  newProfile = new KbProfile(this, getKeyMap(), guid, modified);
517  hwLoading[0] = true;
518  cmd.write(QString("@%1 get :hwprofilename\n").arg(notifyNumber).toLatin1());
519  cmd.flush();
520  } else {
521  // If it's been updated, fetch its name
522  if(newProfile->id().hwModifiedString() != modified){
523  newProfile->id().modifiedString(modified);
524  newProfile->id().hwModifiedString(modified);
525  newProfile->setNeedsSave();
526  if(hwLoading[0]){
527  cmd.write(QString("@%1 get :hwprofilename\n").arg(notifyNumber).toLatin1());
528  cmd.flush();
529  }
530  } else {
531  hwLoading[0] = false;
532  }
533  }
534  hwProfile(newProfile);
535  emit profileAdded();
537  emit profileChanged();
538  } else if(components[0] == "hwprofilename"){
539  // Hardware profile name
540  QString name = QUrl::fromPercentEncoding(components[1].toUtf8());
541  if(!_hwProfile || !hwLoading[0])
542  return;
543  QString oldName = _hwProfile->name();
544  if(!(oldName.length() >= name.length() && oldName.left(name.length()) == name)){
545  // Don't change the name if it's a truncated version of what we already have
546  _hwProfile->name(name);
547  emit profileRenamed();
548  }
549  } else if(components[0] == "mode"){
550  // Mode-specific data
551  if(components.count() < 4)
552  return;
553  int mode = components[1].toInt() - 1;
554  if(components[2] == "hwid"){
555  if(components.count() < 5 || mode >= HWMODE_MAX || !_hwProfile)
556  return;
557  // Hardware mode ID
558  QString guid = components[3];
559  QString modified = components[4];
560  // Look for this mode in the hardware profile
561  KbMode* hwMode = 0;
562  bool isUpdated = false;
563  foreach(KbMode* kbMode, _hwProfile->modes()){
564  if(kbMode->id().guid == guid){
565  hwMode = kbMode;
566  if(kbMode->id().hwModifiedString() != modified){
567  // Update modification time
568  hwMode->id().modifiedString(modified);
569  hwMode->id().hwModifiedString(modified);
570  hwMode->setNeedsSave();
571  isUpdated = true;
572  } else {
573  hwLoading[mode + 1] = false;
574  }
575  break;
576  }
577  }
578  // If it wasn't found, add it
579  if(!hwMode){
580  isUpdated = true;
581  hwMode = new KbMode(this, getKeyMap(), guid, modified);
582  _hwProfile->append(hwMode);
583  // If the hardware profile now contains enough modes to be added to the list, do so
584  if(!_profiles.contains(_hwProfile) && _hwProfile->modeCount() >= hwModeCount){
585  _profiles.append(_hwProfile);
586  _needsSave = true;
587  emit profileAdded();
588  if(!_currentProfile)
590  }
591  }
592  if(hwLoading[mode + 1] && isUpdated){
593  // If the mode isn't in the right place, move it
594  int index = _hwProfile->indexOf(hwMode);
595  if(mode < _hwProfile->modeCount() && index != mode)
596  _hwProfile->move(index, mode);
597  // Fetch the updated data
598  cmd.write(QString("@%1 mode %2 get :hwname :hwrgb").arg(notifyNumber).arg(mode + 1).toLatin1());
599  if(isMouse())
600  cmd.write(" :hwdpi :hwdpisel :hwlift :hwsnap");
601  cmd.write("\n");
602  cmd.flush();
603  }
604  } else if(components[2] == "hwname"){
605  // Mode name - update list
606  if(!_hwProfile || _hwProfile->modeCount() <= mode || mode >= HWMODE_MAX || !hwLoading[mode + 1])
607  return;
608  KbMode* hwMode = _hwProfile->modes()[mode];
609  QString name = QUrl::fromPercentEncoding(components[3].toUtf8());
610  QString oldName = hwMode->name();
611  if(!(oldName.length() >= name.length() && oldName.left(name.length()) == name)){
612  // Don't change the name if it's a truncated version of what we already have
613  hwMode->name(name);
615  emit modeRenamed();
616  }
617  } else if(components[2] == "hwrgb"){
618  // RGB - set mode lighting
619  if(!_hwProfile || _hwProfile->modeCount() <= mode || mode >= HWMODE_MAX || !hwLoading[mode + 1])
620  return;
621  KbMode* kbmode = _hwProfile->modes()[mode];
622  KbLight* light = kbmode->light();
623  // Scan the input for colors
624  QColor lightColor = QColor();
625  for(int i = 3; i < components.count(); i++){
626  QString comp = components[i];
627  if(comp.indexOf(":") < 0){
628  // No ":" - single hex constant
629  bool ok;
630  int rgb = comp.toInt(&ok, 16);
631  if(ok)
632  light->color(QColor::fromRgb((QRgb)rgb));
633  } else {
634  // List of keys ("a,b:xxxxxx"). Parse color first
635  QStringList set = comp.split(":");
636  bool ok;
637  int rgb = set[1].toInt(&ok, 16);
638  if(ok){
639  QColor color = QColor::fromRgb((QRgb)rgb);
640  // Parse keys
641  QStringList keys = set[0].split(",");
642  foreach(QString key, keys){
643  if(key == "light")
644  // Extrapolate the Light key to the M-keys and Lock key, since those will be set to black on hwsave
645  lightColor = color;
646  if(key.startsWith("dpi") && key.length() > 3){
647  // DPI levels go to the KbPerf object instead of KbLight
648  bool ok = false;
649  int index = key.mid(3).toInt(&ok);
650  if(ok)
651  kbmode->perf()->dpiColor(index, color);
652  continue;
653  }
654  light->color(key, color);
655  }
656  }
657  }
658  }
659  if(lightColor.isValid()){
660  light->color("mr", lightColor);
661  light->color("m1", lightColor);
662  light->color("m2", lightColor);
663  light->color("m3", lightColor);
664  light->color("lock", lightColor);
665  }
666  } else if(components[2] == "hwdpi"){
667  // DPI settings
668  if(!_hwProfile || _hwProfile->modeCount() <= mode || mode >= HWMODE_MAX || !hwLoading[mode + 1])
669  return;
670  KbPerf* perf = _hwProfile->modes()[mode]->perf();
671  // Read the rest of the line as stage:x,y
672  foreach(QString comp, components.mid(3)){
673  QStringList dpi = comp.split(':');
674  if(dpi.length() != 2)
675  continue;
676  QStringList xy = dpi[1].split(',');
677  int x, y;
678  bool off = false;
679  if(xy.length() < 2){
680  // If the right side only has one parameter, set both X and Y
681  if(xy[0] == "off")
682  off = true;
683  else
684  x = y = xy[0].toInt();
685  } else {
686  x = xy[0].toInt();
687  y = xy[1].toInt();
688  }
689  // Set DPI for this stage
690  int index = dpi[0].toInt();
691  if(off){
692  perf->dpiEnabled(index, false);
693  // If all DPIs have been disabled, turn them back on
694  bool allOff = true;
695  for(int i = 1; i < KbPerf::DPI_COUNT; i++){
696  if(perf->dpiEnabled(i)){
697  allOff = false;
698  break;
699  }
700  }
701  if(allOff){
702  for(int i = 1; i < KbPerf::DPI_COUNT; i++)
703  perf->dpiEnabled(i, true);
704  }
705  } else {
706  perf->dpiEnabled(index, true);
707  perf->dpi(index, QPoint(x, y));
708  }
709  }
710  } else if(components[2] == "hwdpisel"){
711  // Hardware DPI selection (0...5)
712  if(!_hwProfile || _hwProfile->modeCount() <= mode || mode >= HWMODE_MAX || !hwLoading[mode + 1])
713  return;
714  KbPerf* perf = _hwProfile->modes()[mode]->perf();
715  int idx = components[3].toInt();
716  if(idx < 1)
717  idx = 1;
718  if(idx >= KbPerf::DPI_COUNT)
719  idx = KbPerf::DPI_COUNT - 1;
720  perf->baseDpiIdx(idx);
721  } else if(components[2] == "hwlift"){
722  // Mouse lift height (1...5)
723  if(!_hwProfile || _hwProfile->modeCount() <= mode || mode >= HWMODE_MAX || !hwLoading[mode + 1])
724  return;
725  KbPerf* perf = _hwProfile->modes()[mode]->perf();
726  perf->liftHeight((KbPerf::height)components[3].toInt());
727  } else if(components[3] == "hwsnap"){
728  // Mouse angle snapping ("on" or "off")
729  if(!_hwProfile || _hwProfile->modeCount() <= mode || mode >= HWMODE_MAX || !hwLoading[mode + 1])
730  return;
731  KbPerf* perf = _hwProfile->modes()[mode]->perf();
732  perf->angleSnap(components[3] == "on");
733  }
734  } else if(components[0] == "fwupdate"){
735  // Firmware update progress
736  if(components.count() < 3)
737  return;
738  // Make sure path is the same
739  if(components[1] != fwUpdPath)
740  return;
741  QString res = components[2];
742  if(res == "invalid" || res == "fail")
743  emit fwUpdateFinished(false);
744  else if(res == "ok")
745  emit fwUpdateFinished(true);
746  else {
747  // "xx/yy" indicates progress
748  if(!res.contains("/"))
749  return;
750  QStringList numbers = res.split("/");
751  emit fwUpdateProgress(numbers[0].toInt(), numbers[1].toInt());
752  }
753  }
754 }
755 
757  return KeyMap(_model, _layout);
758 }
759 
760 void Kb::setCurrentProfile(KbProfile *profile, bool spontaneous){
761  while(profile->modeCount() < hwModeCount)
762  profile->append(new KbMode(this, getKeyMap()));
763  KbMode* mode = profile->currentMode();
764  if(!mode)
765  profile->currentMode(mode = profile->modes().first());
766  setCurrentMode(profile, mode, spontaneous);
767 }
768 
769 void Kb::setCurrentMode(KbProfile* profile, KbMode* mode, bool spontaneous){
770  if(_currentProfile != profile){
771  _currentProfile = profile;
772  _needsSave = true;
773  emit profileChanged();
774  }
775  if(_currentMode != mode || _currentProfile->currentMode() != mode){
777  _needsSave = true;
778  emit modeChanged(spontaneous);
779  }
780 }
781 
787 void Kb::macroDelay(bool flag) {
788  _delay = flag;
789 
790  foreach(Kb* kb, activeDevices){
791  kb->cmd.write(QString("\ndelay %1\n").arg(flag? "on" : "off").toLatin1());
792  }
793 }
794 
KbMode * currentMode() const
Definition: kbprofile.h:54
Layout
Definition: keymap.h:70
static KeyMap::Layout layout()
Definition: kb.h:30
void modeRenamed()
Definition: moc_kb.cpp:251
static bool mouseAccel()
Definition: kb.h:39
void setValue(const QString &key, const QVariant &value)
const ModeList & modes() const
Definition: kbprofile.h:42
UsbId & id()
Definition: kbmode.h:52
QUuid guid
Definition: kbmode.h:12
rgb * current
Definition: main.c:46
void setCurrentMode(KbProfile *profile, KbMode *mode, bool spontaneous=true)
Definition: kb.cpp:769
QString prefsPath
Definition: kb.h:177
void infoUpdated()
Definition: moc_kb.cpp:233
int hwModeCount
Definition: kb.h:49
KbBind * bind()
Definition: kbmode.h:61
QString modifiedString() const
Definition: kbmode.h:22
float y
Definition: main.c:66
void profileChanged()
Definition: moc_kb.cpp:257
bool needsSave() const
Definition: kbprofile.cpp:68
void load()
Definition: kb.cpp:241
static int frameRate()
Definition: kb.h:27
QList< KbProfile * > _profiles
Definition: kb.h:168
void frameUpdate(QFile &cmd, bool monochrome=false)
Definition: kblight.cpp:296
bool dpiEnabled(int index) const
Definition: kbperf.h:68
void updateLayout()
Definition: kb.cpp:186
static QSet< QString > notifyPaths
Definition: kb.cpp:12
QString macroPath
Definition: kb.h:155
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
cmd
Definition: command.h:7
bool isStarted()
Definition: kblight.cpp:169
static bool macroDelay()
Definition: kb.h:36
void writeProfileHeader()
Definition: kb.cpp:354
float x
Definition: main.c:66
QString pollrate
Definition: kb.h:18
QColor dpiColor(int index) const
Definition: kbperf.h:85
KbLight * light()
Definition: kbmode.h:60
bool iState[KbPerf::HW_I_COUNT]
Definition: kb.h:174
static Model getModel(const QString &name)
Definition: keymap.cpp:775
KbMode * _currentMode
Definition: kb.h:169
static bool _mouseAccel
Definition: kb.h:165
QString guidString() const
Definition: kbmode.h:20
void save(CkbSettings &settings)
Definition: kbprofile.cpp:48
Definition: keymap.h:49
quint32 modified
Definition: kbmode.h:13
static int scrollSpeed()
Definition: kb.h:42
QString firmware
Definition: kb.h:18
QString cmdpath
Definition: kb.h:155
static int _frameRate
Definition: kb.h:164
height liftHeight() const
Definition: kbperf.h:38
bool _needsSave
Definition: kb.h:201
static QSet< Kb * > activeDevices
Definition: kb.cpp:10
QString devpath
Definition: kbmanager.cpp:4
bool isMouse() const
Definition: kb.h:24
~Kb()
Definition: kb.cpp:141
Definition: main.c:42
void setCurrentProfile(KbProfile *profile, bool spontaneous=true)
Definition: kb.cpp:760
KbProfile * prevProfile
Definition: kb.h:185
void frameUpdate()
Definition: kb.cpp:375
height
Definition: kbperf.h:31
void animKeypress(const QString &key, bool down)
Definition: kblight.cpp:189
UsbId & id()
Definition: kbprofile.h:30
void append(KbMode *newMode)
Definition: kbprofile.h:44
void fwUpdate(const QString &path)
Definition: kb.cpp:365
QPoint dpi(int index) const
Definition: kbperf.h:50
bool monochrome
Definition: kb.h:19
Definition: kbmode.h:36
static const int HWMODE_MAX
Definition: kb.h:50
void close()
Definition: kblight.cpp:215
int modeCount() const
Definition: kbprofile.h:49
Definition: kbperf.h:15
QFile cmd
Definition: kb.h:191
static bool dither()
Definition: kb.h:33
Definition: kb.h:11
static QMutex notifyPathMutex
Definition: kb.cpp:13
int macroNumber
Definition: kb.h:196
void hwSave()
Definition: kb.cpp:297
static const int DPI_COUNT
Definition: kbperf.h:46
Definition: kbbind.h:20
void color(const QString &key, const QColor &newColor)
Definition: kblight.cpp:49
void setNeedsSave()
Definition: kbmode.h:67
void profileRenamed()
Definition: moc_kb.cpp:245
quint64 lastAutoSave
Definition: kb.h:178
QString usbModel
Definition: kb.h:16
void deleteHw()
Definition: kb.cpp:441
void update(QFile &cmd, bool force=false)
Definition: kbbind.cpp:197
bool angleSnap() const
Definition: kbperf.h:42
bool isISO() const
Definition: keymap.h:95
quint32 hwModified
Definition: kbmode.h:14
void keyEvent(const QString &key, bool down)
Definition: kbbind.cpp:296
KbProfile * hwProfile()
Definition: kb.h:46
static bool _dither
Definition: kb.h:165
void update(QFile &cmd, int notifyNumber, bool force, bool saveCustomDpi)
Definition: kbperf.cpp:385
void open()
Definition: kblight.cpp:200
const QString & name() const
Definition: kbmode.h:50
const KeyMap & keyMap() const
Definition: kbprofile.h:37
int baseDpiIdx() const
Definition: kbperf.h:61
Definition: keymap.h:49
void run()
Definition: kb.cpp:446
QString features
Definition: kb.h:18
QString usbSerial
Definition: kb.h:16
static bool _delay
Definition: kb.h:198
static int _scrollSpeed
Definition: kb.h:164
bool needsSave() const
Definition: kb.cpp:344
void profileAdded()
Definition: moc_kb.cpp:239
bool isOpen() const
Definition: kb.h:147
void modeChanged(bool spontaneous)
Definition: moc_kb.cpp:263
void applyIndicators(int modeIndex, const bool indicatorState[HW_I_COUNT])
Definition: kbperf.cpp:439
bool hwLoading[HWMODE_MAX+1]
Definition: kb.h:205
void fwUpdateProgress(int current, int total)
Definition: moc_kb.cpp:270
void move(int from, int to)
Definition: kbprofile.h:47
static KeyMap::Layout _layout
Definition: kb.h:161
void fwUpdateFinished(bool succeeded)
Definition: moc_kb.cpp:277
int indexOf(KbMode *mode) const
Definition: kbprofile.h:50
void base(QFile &cmd, bool ignoreDim=false, bool monochrome=false)
Definition: kblight.cpp:378
QString fwUpdPath
Definition: kb.h:181
void save()
Definition: kb.cpp:273
void setNeedsSave()
Definition: kbprofile.h:25
int notifyNumber
notifyNumber is the trailing number in the device path.
Definition: kb.h:194
QString name() const
Definition: kbprofile.h:28
KbMode * prevMode
Definition: kb.h:186
QString notifyPath
Definition: kb.h:155
Kb(QObject *parent, const QString &path)
Definition: kb.cpp:19
KbProfile * _hwProfile
Definition: kb.h:183
KbPerf * perf()
Definition: kbmode.h:62
static void fps(int framerate)
Definition: kbmanager.cpp:38
KeyMap getKeyMap()
Definition: kb.cpp:756
void autoSave()
Definition: kb.cpp:289
void deletePrevious()
Definition: kb.cpp:426
KbProfile * _currentProfile
Definition: kb.h:167
void readNotify(QString line)
Definition: kb.cpp:471
static bool isBusy()
Definition: ckbsettings.cpp:42
KbProfile * newProfile()
Definition: kb.h:77
QString hwModifiedString() const
Definition: kbmode.h:24