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