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
keyaction.cpp
Go to the documentation of this file.
1 #include <qdebug.h>
2 #include "keyaction.h"
3 #include "kb.h"
4 #include "kbanim.h"
5 #include "kbprofile.h"
6 #include <QDateTime>
7 #include <QUrl>
8 #include <cstring>
9 #include <cstdio>
10 #ifdef USE_LIBX11
11 #include <X11/Xlib.h>
12 #endif
13 
15  if(_value.isEmpty())
16  return UNBOUND;
17  if(_value.at(0) == '$')
18  return SPECIAL;
19  return NORMAL;
20 }
21 
22 KeyAction::KeyAction(const QString &action, QObject *parent)
23  : QObject(parent), _value(action), preProgram(0), relProgram(0), sniperValue(0)
24 {
25 }
26 
28  : QObject(parent), _value(""), preProgram(0), relProgram(0), sniperValue(0)
29 {
30 }
31 
33  // Clean up processes
34  if(preProgram){
35  preProgram->kill();
36  delete preProgram;
37  }
38  if(relProgram){
39  relProgram->kill();
40  delete relProgram;
41  }
42 }
43 
44 QString KeyAction::defaultAction(const QString& key, KeyMap::Model model){
45  // G1-G18 are unbound by default
46  if(key.length() >= 2 && key[0] == 'g'
47  && ((key.length() == 2 && key[1] >= '0' && key[1] <= '9')
48  || (key.length() == 3 && key[1] == '1' && key[2] >= '0' && key[2] <= '8')))
49  return "";
50  // So are thumbgrid buttons
51  if(key.startsWith("thumb"))
52  return "";
53  // TODO: default action for MR
54  if(key == "mr")
55  return "";
56  // M1-M3 switch modes
57  if(key == "m1")
58  return "$mode:0";
59  if(key == "m2")
60  return "$mode:1";
61  if(key == "m3")
62  return "$mode:2";
63  // Brightness and Win Lock are their own functions
64  if(key == "light")
65  return "$light:2";
66  if(key == "lock")
67  return "$lock:0";
68  // DPI buttons
69  if(key == "dpiup"){
70  if(model == KeyMap::HARPOON ||
71  model == KeyMap::GLAIVE){
72  return "$dpi:-4";
73  }
74  return "$dpi:-2";
75  }
76  if(key == "dpidn")
77  return "$dpi:-1";
78  if(key == "sniper")
79  return "$dpi:0";
80  // Everything else is a standard keypress
81  return key;
82 }
83 
84 QString KeyAction::friendlyName(const KeyMap& map) const {
85  if(_value.isEmpty())
86  return "Unbound";
87  QStringList parts = _value.split(":");
88  QString prefix = parts[0];
89  if(parts.length() < 2){
90  KeyMap::Layout layout = map.layout();
91  QString name = KeyMap::friendlyName(_value, layout);
92  if(name.isEmpty())
93  return "(Unknown)";
94  return name;
95  }
96  int suffix = parts[1].toInt();
97  if(prefix == "$mode"){
98  switch(suffix){
99  case MODE_PREV:
100  case MODE_PREV_WRAP:
101  return "Switch to previous mode";
102  case MODE_NEXT:
103  case MODE_NEXT_WRAP:
104  return "Switch to next mode";
105  default:
106  return tr("Switch to mode %1").arg(suffix + 1);
107  }
108  } else if(prefix == "$dpi"){
109  // Split off custom parameters (if any)
110  int level = parts[1].split("+")[0].toInt();
111  switch(level){
112  case DPI_CYCLE_UP:
113  return "DPI cycle up";
114  case DPI_CYCLE_DOWN:
115  return "DPI cycle down";
116  case DPI_UP:
117  return "DPI up";
118  case DPI_DOWN:
119  return "DPI down";
120  case DPI_SNIPER:
121  return "Sniper";
122  case DPI_CUSTOM:{
123  QPoint xy;
124  dpiInfo(xy);
125  return tr("DPI: %1, %2").arg(xy.x()).arg(xy.y());
126  }
127  default:
128  return tr("DPI stage %1").arg(level);
129  }
130  } else if(prefix == "$light"){
131  switch(suffix){
132  case LIGHT_UP:
133  case LIGHT_UP_WRAP:
134  return "Brightness up";
135  case LIGHT_DOWN:
136  case LIGHT_DOWN_WRAP:
137  return "Brightness down";
138  }
139  } else if(prefix == "$lock"){
140  switch(suffix){
141  case LOCK_TOGGLE:
142  return "Toggle Windows lock";
143  case LOCK_ON:
144  return "Windows lock on";
145  case LOCK_OFF:
146  return "Windows lock off";
147  }
148  } else if(prefix == "$anim"){
149  return "Start animation";
150  } else if(prefix == "$program"){
151  return "Launch program";
152  } else if(prefix == "$macro"){
153  return "Send G-key macro";
154  }
155  return "(Unknown)";
156 }
157 
158 QString KeyAction::modeAction(int mode){
159  return QString("$mode:%1").arg(mode);
160 }
161 
162 QString KeyAction::dpiAction(int level, int customX, int customY){
163  QString action = tr("$dpi:%1").arg(level);
164  if(level == DPI_CUSTOM)
165  action += tr("+%1+%2").arg(customX).arg(customY);
166  return action;
167 }
168 
169 QString KeyAction::lightAction(int type){
170  return QString("$light:%1").arg(type);
171 }
172 
173 QString KeyAction::lockAction(int type){
174  return QString("$lock:%1").arg(type);
175 }
176 
177 QString KeyAction::programAction(const QString& onPress, const QString& onRelease, int stop){
178  // URL-encode the commands and place them in the string (":" and "+" are both replaced, so they won't interfere)
179  return "$program:" + QString::fromUtf8(QUrl::toPercentEncoding(onPress.trimmed())) + "+" + QString::fromUtf8(QUrl::toPercentEncoding(onRelease.trimmed())) + QString("+%1").arg(stop);
180 }
181 
182 const static int ANIM_ONCE = 0x1, ANIM_KRSTOP = 0x2;
183 
184 QString KeyAction::animAction(const QUuid& guid, bool onlyOnce, bool stopOnRelease){
185  int flags = (onlyOnce ? ANIM_ONCE : 0) | (stopOnRelease ? ANIM_KRSTOP : 0);
186  return "$anim:" + guid.toString() + QString("+%1").arg(flags);
187 }
188 
189 QString KeyAction::specialInfo(int& parameter) const {
190  QStringList list = _value.split(":");
191  if(list.length() < 2){
192  parameter = INT_MIN;
193  return "";
194  }
195  parameter = list[1].toInt();
196  return list[0].replace("$", "");
197 }
198 
199 int KeyAction::programInfo(QString& onPress, QString& onRelease) const {
200  if(!isProgram())
201  return 0;
202  QString param = _value.mid(9);
203  QStringList programs = param.split("+");
204  if(programs.length() != 3)
205  return 0;
206  onPress = QUrl::fromPercentEncoding(programs[0].toUtf8());
207  onRelease = QUrl::fromPercentEncoding(programs[1].toUtf8());
208  return programs[2].toInt();
209 }
210 
211 int KeyAction::dpiInfo(QPoint& custom) const {
212  if(!isDPI())
213  return 0;
214  QString param = _value.mid(5);
215  QStringList lxy = param.split("+");
216  int level = lxy[0].toInt();
217  if(level == DPI_CUSTOM){
218  if(lxy.length() != 3)
219  return 0;
220  custom = QPoint(lxy[1].toInt(), lxy[2].toInt());
221  }
222  return level;
223 }
224 
225 QUuid KeyAction::animInfo(bool &onlyOnce, bool &stopOnRelease) const {
226  if(!isAnim())
227  return QUuid();
228  QString param = _value.mid(6);
229  QStringList split = param.split("+");
230  if(split.length() < 2)
231  return QUuid();
232  QUuid id = split[0];
233  int flags = split[1].toInt();
234  onlyOnce = !!(flags & ANIM_ONCE);
235  stopOnRelease = !!(flags & ANIM_KRSTOP);
236  return id;
237 }
238 
239 QString KeyAction::driverName() const {
240  if(isSpecial())
241  return "";
242  return _value;
243 }
244 
245 void KeyAction::keyEvent(KbBind* bind, bool down){
246  // No need to respond to standard actions
247  if(!isSpecial())
248  return;
249  QStringList parts = _value.split(":");
250  if(parts.length() < 2)
251  return;
252  QString prefix = parts[0];
253  int suffix = parts[1].toInt();
254  if(prefix == "$mode"){
255  if(!down)
256  return;
257  // Change mode
258  Kb* device = bind->devParent();
259  KbProfile* currentProfile = device->currentProfile();
260  int mode = currentProfile->indexOf(currentProfile->currentMode());
261  int modeCount = currentProfile->modeCount();
262  switch(suffix){
263  case MODE_PREV_WRAP:
264  mode--;
265  if(mode < 0)
266  mode = modeCount - 1;
267  break;
268  case MODE_NEXT_WRAP:
269  mode++;
270  if(mode >= modeCount)
271  mode = 0;
272  break;
273  case MODE_PREV:
274  mode--;
275  break;
276  case MODE_NEXT:
277  mode++;
278  break;
279  default:
280  // Absolute
281  mode = suffix;
282  break;
283  }
284  if(mode < 0 || mode >= modeCount)
285  return;
286  device->setCurrentMode(currentProfile->modes()[mode]);
287  } else if(prefix == "$dpi"){
288  KbPerf* perf = bind->perf();
289  int level = parts[1].split("+")[0].toInt();
290  switch(level){
291  case DPI_CYCLE_UP:
292  if(!down)
293  return;
294  perf->dpiCycleUp();
295  break;
296  case DPI_CYCLE_DOWN:
297  if(!down)
298  return;
299  perf->dpiCycleDown();
300  break;
301  case DPI_UP:
302  if(!down)
303  return;
304  perf->dpiUp();
305  break;
306  case DPI_DOWN:
307  if(!down)
308  return;
309  perf->dpiDown();
310  break;
311  case DPI_SNIPER:
312  if(down)
313  sniperValue = perf->pushSniper();
314  else {
315  perf->popDpi(sniperValue);
316  sniperValue = 0;
317  }
318  break;
319  case DPI_CUSTOM:{
320  QPoint xy;
321  dpiInfo(xy);
322  if(xy.x() <= 0 || xy.y() <= 0)
323  break;
324  if(down)
325  sniperValue = perf->pushDpi(xy);
326  else {
327  perf->popDpi(sniperValue);
328  sniperValue = 0;
329  }
330  break;
331  }
332  default:
333  if(level < 1 || level >= KbPerf::DPI_COUNT
334  || !down)
335  return;
336  perf->baseDpiIdx(level);
337  break;
338  }
339  } else if(prefix == "$light"){
340  if(!down)
341  return;
342  // Change brightness
343  KbLight* light = bind->light();
344  int dim = light->dimming();
345  switch(suffix){
346  case LIGHT_UP:
347  if(dim > 0)
348  dim--;
349  break;
350  case LIGHT_DOWN:
351  if(dim < KbLight::MAX_DIM)
352  dim++;
353  break;
354  case LIGHT_UP_WRAP:
355  dim--;
356  if(dim < 0)
357  dim = KbLight::MAX_DIM;
358  break;
359  case LIGHT_DOWN_WRAP:
360  dim++;
361  if(dim > KbLight::MAX_DIM)
362  dim = 0;
363  break;
364  }
365  light->dimming(dim);
366  } else if(prefix == "$lock"){
367  if(!down)
368  return;
369  // Change win lock
370  switch(suffix){
371  case LOCK_TOGGLE:
372  bind->winLock(!bind->winLock());
373  break;
374  case LOCK_ON:
375  bind->winLock(true);
376  break;
377  case LOCK_OFF:
378  bind->winLock(false);
379  break;
380  }
381  } else if(prefix == "$anim"){
382  // Start animation
383  bool onlyOnce = false, stopOnRelease = false;
384  QUuid id = animInfo(onlyOnce, stopOnRelease);
385  KbAnim* anim = bind->light()->findAnim(id);
386  if(!anim)
387  return;
388  if(down){
389  if(!onlyOnce || !anim->isActive())
390  // If "only once" is enabled, don't start the animation when it's already running
391  anim->trigger(QDateTime::currentMSecsSinceEpoch(), true);
392  } else if(stopOnRelease){
393  // Key released - stop animation
394  anim->stop();
395  }
396  } else if(prefix == "$program"){
397  // Launch program
398  QString onPress, onRelease;
399  int stop = programInfo(onPress, onRelease);
400  // Stop running programs based on setting
401  QProcess* process = 0;
402  if(down){
403  if(stop & PROGRAM_PR_KPSTOP){
404  process = preProgram;
405  if(process)
406  process->kill();
407  process = 0;
408  }
409  if(stop & PROGRAM_RE_KPSTOP)
410  process = relProgram;
411  } else {
412  if(stop & PROGRAM_PR_KRSTOP)
413  process = preProgram;
414  }
415  if(process)
416  process->kill();
417  // Launch new process if requested
418  QString& program = down ? onPress : onRelease;
419  if(program.isEmpty())
420  return;
421  // Check if the program is running already. If so, don't start it again.
422  process = down ? preProgram : relProgram;
423  if(process){
424  if(process->state() == QProcess::NotRunning)
425  delete process;
426  else
427  return;
428  }
429 
430  // Adjust the selected display.
431  adjustDisplay();
432 
433  // Start the program. Wrap it around sh to parse arguments.
434  if((down && (stop & PROGRAM_PR_MULTI))
435  || (!down && (stop & PROGRAM_RE_MULTI))){
436  // Multiple instances allowed? Start detached process
437  QProcess::startDetached("sh", QStringList() << "-c" << program);
438  } else {
439  process = new QProcess(this);
440  process->start("sh", QStringList() << "-c" << program);
441  if(down)
442  preProgram = process;
443  else
444  relProgram = process;
445  }
446  } else if (prefix == "$macro") {
447  // Do nothing, because all work is done by the keyboard itself.
448  // For now, there is no reason to react on G-key press or release.
449  // If u find some reason, then here is the place for it.
450  }
451 }
452 
457  qDebug() << "isMacro returns" << (isMacro() ? "true" : "false");
458  qDebug() << "isValidMacro returns" << (isValidMacro() ? "true" : "false");
459  QStringList ret =_value.split(":");
460  qDebug() << "Macro definition conains" << ret.count() << "elements";
461  qDebug() << "Macro definition is" << _value;
462 }
463 
465 #ifdef USE_LIBX11
466  // Try to get the current display from the X server
467  char* display_name = XDisplayName(NULL);
468  if(!display_name)
469  return;
470  Display* display = XOpenDisplay(display_name);
471  if(!display)
472  return;
473  char* display_string = DisplayString(display);
474  if(!display_string || strlen(display_string) == 0){
475  XCloseDisplay(display);
476  return;
477  }
478  size_t envstr_size = strlen(display_string) + 4;
479  char* envstr = new char[envstr_size];
480  strncpy(envstr, display_string, envstr_size);
481  envstr[envstr_size - 1] = 0;
482 
483  Window root_window = XRootWindow(display, DefaultScreen(display));
484  Window root_window_ret, child_window_ret, window;
485  XWindowAttributes attr;
486  int root_x, root_y, win_x, win_y;
487  unsigned int mask_ret;
488 
489  // Find the screen which currently has the mouse
490  XQueryPointer(display, root_window, &root_window_ret, &child_window_ret, &root_x, &root_y, &win_x, &win_y, &mask_ret);
491  if(child_window_ret == (Window)NULL)
492  window = root_window_ret;
493  else
494  window = child_window_ret;
495  XGetWindowAttributes(display, window, &attr);
496 
497  char* ptr = strchr(envstr, ':');
498  if(ptr){
499  ptr = strchr(ptr, '.');
500  if(ptr)
501  *ptr = '\0';
502  char buf[16];
503  snprintf(buf, sizeof(buf), ".%i", XScreenNumberOfScreen(attr.screen));
504  strncat(envstr, buf, envstr_size - 1 - strlen(envstr));
505 
506  // Update environment variable
507  setenv("DISPLAY", envstr, 1);
508  }
509 
510  delete[] envstr;
511  XCloseDisplay(display);
512 #endif
513 }
514 
523 QString KeyAction::macroAction(QString macroDef) {
524  return QString ("$macro:%1").arg(macroDef);
525 }
KbMode * currentMode() const
Definition: kbprofile.h:54
Layout
Definition: keymap.h:70
const ModeList & modes() const
Definition: kbprofile.h:42
void stop()
Definition: kbanim.cpp:312
void dpiCycleUp()
Definition: kbperf.cpp:317
void setCurrentMode(KbProfile *profile, KbMode *mode, bool spontaneous=true)
Definition: kb.cpp:769
bool isProgram() const
Definition: keyaction.h:158
QProcess * preProgram
Definition: keyaction.h:192
static const int DPI_CYCLE_DOWN
Definition: keyaction.h:126
static const int DPI_UP
Definition: keyaction.h:127
bool isMacro() const
Definition: keyaction.h:160
static QString animAction(const QUuid &guid, bool onlyOnce, bool stopOnRelease)
Definition: keyaction.cpp:184
static QString macroAction(QString macroDef)
well documented in cpp file
Definition: keyaction.cpp:523
static QString friendlyName(const QString &key, Layout layout=US)
Definition: keymap.cpp:925
static const int DPI_CUSTOM
Definition: keyaction.h:128
static const int PROGRAM_PR_MULTI
Definition: keyaction.h:138
static const int ANIM_ONCE
Definition: keyaction.cpp:182
bool isActive() const
Definition: kbanim.h:63
KbPerf * perf()
Definition: kbbind.cpp:50
Kb * devParent() const
Definition: kbbind.h:115
static const int PROGRAM_RE_MULTI
Definition: keyaction.h:139
static QString dpiAction(int level, int customX=0, int customY=0)
Definition: keyaction.cpp:162
QString specialInfo(int &parameter) const
Definition: keyaction.cpp:189
bool isValidMacro() const
isValidMacro checks whether a keyAction contains a valid macro. This is done easily: If the macro act...
Definition: keyaction.h:55
struct parameter contains the values for a fresh started macro_play thread. parameter_t is the typede...
Definition: input.h:54
void trigger(quint64 timestamp, bool ignoreParameter=false)
Definition: kbanim.cpp:209
KbProfile * currentProfile()
Definition: kb.h:56
static const int ANIM_KRSTOP
Definition: keyaction.cpp:182
static const int MODE_PREV
Definition: keyaction.h:122
static QString programAction(const QString &onPress, const QString &onRelease, int stop)
Definition: keyaction.cpp:177
static const int PROGRAM_PR_KPSTOP
Definition: keyaction.h:138
bool isDPI() const
Definition: keyaction.h:162
void dpiCycleDown()
Definition: kbperf.cpp:329
QUuid animInfo(bool &onlyOnce, bool &stopOnRelease) const
Definition: keyaction.cpp:225
void dpiDown()
Definition: kbperf.cpp:306
Definition: keymap.h:49
static const int MAX_DIM
Definition: kblight.h:38
KeyAction(const QString &action, QObject *parent=0)
Definition: keyaction.cpp:22
quint64 sniperValue
Definition: keyaction.h:196
void popDpi(quint64 pushIdx)
Definition: kbperf.cpp:281
static const int DPI_SNIPER
Definition: keyaction.h:128
quint64 pushDpi(const QPoint &newDpi)
Definition: kbperf.cpp:274
static const int PROGRAM_RE_KPSTOP
Definition: keyaction.h:139
static QString lockAction(int type=LOCK_TOGGLE)
Definition: keyaction.cpp:173
static const int MODE_PREV_WRAP
Definition: keyaction.h:123
void adjustDisplay()
Definition: keyaction.cpp:464
KbLight * light()
Definition: kbbind.cpp:54
static const int LOCK_ON
Definition: keyaction.h:135
int modeCount() const
Definition: kbprofile.h:49
Definition: kbperf.h:15
QString _value
Definition: keyaction.h:189
Definition: kb.h:11
static const int LOCK_TOGGLE
Definition: keyaction.h:135
static const int DPI_COUNT
Definition: kbperf.h:46
Definition: kbanim.h:11
Layout layout() const
Definition: keymap.h:130
static QString modeAction(int mode)
Definition: keyaction.cpp:158
Definition: kbbind.h:20
bool winLock()
Definition: kbbind.h:79
static const int LIGHT_DOWN
Definition: keyaction.h:131
static const int DPI_CYCLE_UP
Definition: keyaction.h:126
static const int LOCK_OFF
Definition: keyaction.h:135
int dpiInfo(QPoint &custom) const
Definition: keyaction.cpp:211
Model
Definition: keymap.h:51
static const int PROGRAM_PR_KRSTOP
Definition: keyaction.h:138
static QString lightAction(int type=LIGHT_UP_WRAP)
Definition: keyaction.cpp:169
quint64 pushSniper()
Definition: kbperf.h:75
QString driverName() const
Definition: keyaction.cpp:239
static const int MODE_NEXT_WRAP
Definition: keyaction.h:123
bool isAnim() const
Definition: keyaction.h:159
int baseDpiIdx() const
Definition: kbperf.h:61
Definition: keymap.h:49
void macroDisplay()
Debug output for invalid macro Definitions.
Definition: keyaction.cpp:456
void dpiUp()
Definition: kbperf.cpp:295
int programInfo(QString &onPress, QString &onRelease) const
Definition: keyaction.cpp:199
static const int MODE_NEXT
Definition: keyaction.h:122
Type type() const
Definition: keyaction.cpp:14
QString friendlyName(const KeyMap &map) const
Definition: keyaction.cpp:84
int dimming()
Definition: kblight.h:39
static const int LIGHT_DOWN_WRAP
Definition: keyaction.h:132
static const int DPI_DOWN
Definition: keyaction.h:127
int indexOf(KbMode *mode) const
Definition: kbprofile.h:50
QProcess * relProgram
Definition: keyaction.h:193
static const int LIGHT_UP_WRAP
Definition: keyaction.h:132
KbAnim * findAnim(const QUuid &guid) const
Definition: kblight.h:51
static QString defaultAction(const QString &key, KeyMap::Model model)
Definition: keyaction.cpp:44
bool isSpecial() const
Definition: keyaction.h:154
void keyEvent(KbBind *bind, bool down)
Definition: keyaction.cpp:245
static const int LIGHT_UP
Definition: keyaction.h:131
struct keyAnim * anim
Definition: main.c:55