From fdf73e22b992197ada041295b2ecf3e5f03bb857 Mon Sep 17 00:00:00 2001 From: Lutz Eichler Date: Thu, 22 Sep 2016 22:06:39 +0200 Subject: [PATCH] use the new delay functions with macros This implementation adds some UI Elements to the Binding tab. When recording a macro, the timing (the time between changes the Keyboard receives) are noted with the key values. When saved with the detailled timing information, you may choose between - running the macro witghout any delay - running it with the standard delay - 0ms for the first 20 changes (normally 10 Keys), - then 30ms for up to 100 keys (200 status-changes) - and then 200ms for any further change) - running the key delays, which had been recorded. After recording (or after saving) you may change the timing values. This might be useful, if you want to generate slow keystrokes, but you do not want unneccessary long wait times (e.g. when sending longer macro content to a virtual machine or a remote desktop tool, the maximum speed is often too high. So include a short delay and all is fine). --- src/ckb-daemon/input.c | 2 +- src/ckb/keyaction.cpp | 4 +- src/ckb/keyaction.h | 22 ++++++++--- src/ckb/rebindwidget.cpp | 79 +++++++++++++++++++++++++++++----------- src/ckb/rebindwidget.h | 5 +-- src/ckb/rebindwidget.ui | 2 +- 6 files changed, 80 insertions(+), 34 deletions(-) diff --git a/src/ckb-daemon/input.c b/src/ckb-daemon/input.c index 2335170..132385d 100644 --- a/src/ckb-daemon/input.c +++ b/src/ckb-daemon/input.c @@ -39,7 +39,7 @@ static void inputupdate_keys(usbdevice* kb){ usleep(action->delay); } else if (kb->delay != UINT_MAX) { // use default global delay usleep(kb->delay); - } else { // use old long macro delay code + } else if (a < (macro->actioncount - 1)) { // use delays depending on macro length if (a > 200) { usleep (100); } else if (a > 20) { diff --git a/src/ckb/keyaction.cpp b/src/ckb/keyaction.cpp index 0e3ce38..4c872f0 100644 --- a/src/ckb/keyaction.cpp +++ b/src/ckb/keyaction.cpp @@ -498,8 +498,8 @@ void KeyAction::adjustDisplay(){ /// macroAction ist called while being in the macro pane /// and clicking Apply with something in the Macro Text Box. /// It tags that input with "$macro:" for further recognition. -/// \param macroDef holds the String containing parts 2-4 of a complete macro definition. -/// \return QString holding the complete G-Key macro definition (parts 1-4) +/// \param macroDef holds the String containing parts 2-5 of a complete macro definition. +/// \return QString holding the complete G-Key macro definition (parts 1-5) /// QString KeyAction::macroAction(QString macroDef) { return QString ("$macro:%1").arg(macroDef); diff --git a/src/ckb/keyaction.h b/src/ckb/keyaction.h index 82c4d82..0ad3b39 100644 --- a/src/ckb/keyaction.h +++ b/src/ckb/keyaction.h @@ -37,7 +37,7 @@ class KeyAction : public QObject /// returns the complete string except the leading "$" /// (the $ may confuse some caller). /// \return QString - /// All 4 parts are returned in one QString. + /// All 5 parts are returned in one QString. /// If no definition exists, return "" /// inline QString macroFullLine() const { @@ -47,7 +47,7 @@ class KeyAction : public QObject ////////// /// \brief isValidMacro checks whether a keyAction contains a valid macro. /// This is done easily: If the macro action starts with $macro: - /// and has four elements, delimited by ":", we may assume, + /// and has five elements, delimited by ":", we may assume, /// that is a structural correct macro action. /// \return bool as true iff the macro definition contains all four elements. /// @@ -55,7 +55,7 @@ class KeyAction : public QObject if (isMacro()) { QStringList ret; ret =_value.split(":"); - return (ret.count() == 4); + return (ret.count() == 5); } else { return false; } @@ -64,8 +64,10 @@ class KeyAction : public QObject ////////// /// \brief macroLine returns all interresting content for a macro definition. /// \return QStringList returns the Macro Key Definition, - /// Readble Macro String and - /// Readable Macro Comment as QStringList. + /// Readble Macro String, + /// Readable Macro Comment and + /// the original timing information + /// as QStringList. /// inline QStringList macroLine() const { if (isValidMacro()) { @@ -84,6 +86,15 @@ class KeyAction : public QObject return isValidMacro() ? _value.split(":")[1] : ""; } + ////////// + /// \brief macroTiming returns the macro key definition with original timing infos + /// (the fifth and up to now last part of the macro action). + /// \return QString macroTiming + /// + inline QString macroTiming() const { + return isValidMacro() ? _value.split(":")[4] : ""; + } + ////////// /// \brief Debug output for invalid macro Definitions /// @@ -94,6 +105,7 @@ class KeyAction : public QObject /// This sequence will program the keyboard and is hardly readable /// 3. Readable Macro String: This is displayed in pteMacroText /// 4. Readable Macro Comment:This is displayed in pteMacroComment + /// 5. completely unreadable original macro information with timing values /// void macroDisplay(); diff --git a/src/ckb/rebindwidget.cpp b/src/ckb/rebindwidget.cpp index 8c98e06..7b4ed70 100644 --- a/src/ckb/rebindwidget.cpp +++ b/src/ckb/rebindwidget.cpp @@ -16,7 +16,6 @@ RebindWidget::RebindWidget(QWidget *parent) : ui->modeWrapBox->hide(); ui->programKpExtra->hide(); ui->programKrExtra->hide(); - macroSafe = ""; // Populate key lists modKeys << "caps" << "lshift" << "rshift" << "lctrl" << "rctrl" << "lwin" << "rwin" << "lalt" << "ralt" << "rmenu" << "fn"; @@ -193,6 +192,7 @@ void RebindWidget::setSelection(const QStringList& newSelection, bool applyPrevi ui->pteMacroBox->setPlainText(""); ui->pteMacroText->setPlainText(""); ui->pteMacroComment->setPlainText(""); + ui->txtBuffer->setText(""); // Fill in field and select tab according to action type bool mouse = act.isMouse(); if(mouse){ @@ -295,6 +295,12 @@ void RebindWidget::setSelection(const QStringList& newSelection, bool applyPrevi ui->pteMacroBox->setPlainText(act.macroContent()); ui->pteMacroText->setPlainText(act.macroLine()[1].replace("&das_IST_31N_col0n;", ":")); ui->pteMacroComment->setPlainText(act.macroLine()[2].replace("&das_IST_31N_col0n;", ":")); + // Set the invisible Buffer to the original timing information. + // For convenience / Migration from older versions: + // If the timing information is only "x", then ignore it by setting it to an empty QString. + ui->txtBuffer->setText(""); + if (act.macroTiming() != "x") ui->txtBuffer->setText(act.macroTiming()); + setCorrectRadioButton(act.macroContent()); } else { qDebug("RebindWidget::setSelection found invalid macro definition."); act.macroDisplay(); @@ -371,13 +377,25 @@ void RebindWidget::applyChanges(const QStringList& keys, bool doUnbind){ } bind->setAction(keys, KeyAction::programAction(ui->programKpBox->text(), ui->programKrBox->text(), kpStop | krStop)); } else if (ui->pteMacroBox->toPlainText().length() > 0) { - // G-key macro handling: - // Set the macro definiton for all keys selected (indeed, it may be multiple keys). - // First, concat the Macro Key Definion and the Macro plain text - // after escaping possible colos in the parts for Macro Text and Macro Comment. + /// G-key macro handling: + /// Set the macro definiton for all keys selected (indeed, it may be multiple keys). + /// First, concat the Macro Key Definion and the Macro plain text + /// after escaping possible colos in the parts for Macro Text and Macro Comment. + + /// But first, there is a special condition to handle: + /// You have recorded a macro with timing infos. + /// Afterwards you changed manually the timing infos in the pteMacroBox and press Apply. + /// In that case we must overwrite the txtBuffer to remember your changes. + if (ui->rb_delay_asTyped->isChecked()) ui->txtBuffer->setText(ui->pteMacroBox->toPlainText()); + + /// \todo There is still a bug in the state machine: + /// If you record a macro in asTyped-mode, switch to another mode + /// and change the vontent of the pteMacroBox manually, + /// then the changes are not saved in the timing buffer. + /// But anyhow, let's do more relevant things... QString mac; mac = ui->txtBuffer->text(); - mac = ui->pteMacroComment->toPlainText().replace(":", "&das_IST_31N_col0n;");// + ":" + mac; + mac = ui->pteMacroComment->toPlainText().replace(":", "&das_IST_31N_col0n;") + ":" + mac; mac = ui->pteMacroText->toPlainText().replace(":", "&das_IST_31N_col0n;") + ":" + mac; mac = ui->pteMacroBox->toPlainText() + ":" + mac; bind->setAction(keys, KeyAction::macroAction(mac)); @@ -435,6 +453,7 @@ void RebindWidget::setBox(QWidget* box){ // Clear macro panel if (box != ui->pteMacroBox) { ui->pteMacroBox->setPlainText(""); + ui->txtBuffer->setText(""); helpStatus(1); } } @@ -803,23 +822,21 @@ void RebindWidget::convertMacroBox() { // How to deal with the delay params? // Because the three radio buttons are mututally exclusive, // we can run through the if-chain w/o conflicts. + // If rb_delay_asTyped is checked, do nothing, because that's the standard. - if (ui->rb_delay_asTyped->isChecked()) { - // Do nothing, because the timing parameters are added by default into the strings. - } else { - in.replace(QRegExp("=\\d*,"), ","); // Delete the timing infos - in.replace(QRegExp("^delay o..?,"), ""); // Delete older delay defaulting infos, if available - - if (ui->rb_delay_default->isChecked()) { - in = "delay on\n" + in; - } - if (ui->rb_delay_no->isChecked()) { - in = "delay off\n" + in; - } + if (ui->rb_delay_default->isChecked()) { + in.replace(QRegExp("=\\d+,"), ","); // Delete the timing infos, use default value + in.replace(QRegExp("=\\d+$"), ""); // The last entry is without comma + } + if (ui->rb_delay_no->isChecked()) { + in.replace(QRegExp("=\\d+,"), "=0,"); // Set timing infos to zero for no delay + in.replace(QRegExp("=\\d+$"), "=0"); // Again the last entry w/o comma + in.replace(QRegExp("([\\+\\-]\\w+),"), "\\1=0,"); // If no delay is given, force it to zero + in.replace(QRegExp("([\\+\\-]\\w+)$"), "\\1=0"); } + // Show the new format by replacing the older one. ui->pteMacroBox->setPlainText(in); - ui->pteMacroText->setPlainText(ui->txtBuffer->text()); } ////////// @@ -832,7 +849,6 @@ void RebindWidget::convertMacroBox() { /// void RebindWidget::on_rb_delay_no_toggled(bool checked) { - ui->pteMacroText->setPlainText("Delay_no was toggled\n"); convertMacroBox(); } @@ -845,7 +861,6 @@ void RebindWidget::on_rb_delay_no_toggled(bool checked) /// void RebindWidget::on_rb_delay_asTyped_toggled(bool checked) { - ui->pteMacroText->setPlainText("Delay_asTyped was toggled\n"); convertMacroBox(); } @@ -857,6 +872,26 @@ void RebindWidget::on_rb_delay_asTyped_toggled(bool checked) /// void RebindWidget::on_rb_delay_default_toggled(bool checked) { - ui->pteMacroText->setPlainText("Delay_default was toggled\n"); convertMacroBox(); } + +////////// +/// \brief RebindWidget::setCorrectRadioButton +/// \param macdef +/// Set the radiobutton for timing paramters according to +/// the context. +/// If no "=" followed by a number and comma can be found, it is the default button. +/// If "=" can be found and numbers with more than one digit (means: > 9), it is the "asTyped" button +/// Otherwise it is the "no" button. +/// +void RebindWidget::setCorrectRadioButton (QString macdef) { + if (!macdef.contains(QRegExp("=\\d+,"))) { + ui->rb_delay_default->setChecked(true); + return; + } + if (macdef.contains(QRegExp("=\\d\\d+,"))) { + ui->rb_delay_asTyped->setChecked(true); + return; + } + ui->rb_delay_no->setChecked(true); +} diff --git a/src/ckb/rebindwidget.h b/src/ckb/rebindwidget.h index 21247d6..ebb0fec 100644 --- a/src/ckb/rebindwidget.h +++ b/src/ckb/rebindwidget.h @@ -89,13 +89,12 @@ private slots: // Show some help info void helpStatus(int status); + void setCorrectRadioButton (QString macdef); + KbBind* bind; KbProfile* profile; QStringList selection; - // Hold complete macro information while fiddling with delay params - QString macroSafe; - QStringList typingKeys; QStringList modKeys; QStringList fnKeys; diff --git a/src/ckb/rebindwidget.ui b/src/ckb/rebindwidget.ui index a4440ab..dc2683c 100644 --- a/src/ckb/rebindwidget.ui +++ b/src/ckb/rebindwidget.ui @@ -20,7 +20,7 @@ - 5 + 0 false