diff --git a/src/Kaleidoscope-Ranges.h b/src/Kaleidoscope-Ranges.h
index d0135173..dfa4a62b 100644
--- a/src/Kaleidoscope-Ranges.h
+++ b/src/Kaleidoscope-Ranges.h
@@ -49,6 +49,8 @@ enum : uint16_t {
SC_LAST,
REDIAL,
TURBO,
+ MACRO_FIRST,
+ MACRO_LAST = MACRO_FIRST + 255,
DYNAMIC_MACRO_FIRST,
DYNAMIC_MACRO_LAST = DYNAMIC_MACRO_FIRST + 31,
diff --git a/src/kaleidoscope/key_defs.h b/src/kaleidoscope/key_defs.h
index 16f88a37..1b363287 100644
--- a/src/kaleidoscope/key_defs.h
+++ b/src/kaleidoscope/key_defs.h
@@ -160,7 +160,7 @@ typedef kaleidoscope::Key Key_;
#define RALT_HELD B00000100
#define SHIFT_HELD B00001000
#define GUI_HELD B00010000
-// #define IS_MACRO B00100000 // defined in Kaleidoscope-Macros/src/MacroKeyDefs.h
+
#define SYNTHETIC B01000000
#define RESERVED B10000000
diff --git a/src/kaleidoscope/plugin/Macros.cpp b/src/kaleidoscope/plugin/Macros.cpp
index 23ade38f..eb83f648 100644
--- a/src/kaleidoscope/plugin/Macros.cpp
+++ b/src/kaleidoscope/plugin/Macros.cpp
@@ -243,11 +243,18 @@ const macro_t *Macros_::type(const char *string) {
return MACRO_NONE;
}
+bool Macros_::isMacroKey(Key key) {
+ if (key >= ranges::MACRO_FIRST && key <= ranges::MACRO_LAST)
+ return true;
+ return false;
+}
+
EventHandlerResult Macros_::onKeyswitchEvent(Key &mappedKey, KeyAddr key_addr, uint8_t keyState) {
- if (mappedKey.getFlags() != (SYNTHETIC | IS_MACRO))
+ if (! isMacroKey(mappedKey))
return EventHandlerResult::OK;
- addActiveMacroKey(mappedKey.getKeyCode(), key_addr.toInt(), keyState);
+ uint8_t macro_index = mappedKey.getRaw() - ranges::MACRO_FIRST;
+ addActiveMacroKey(macro_index, key_addr.toInt(), keyState);
return EventHandlerResult::EVENT_CONSUMED;
}
diff --git a/src/kaleidoscope/plugin/Macros.h b/src/kaleidoscope/plugin/Macros.h
index ca4aaf03..e0a3d0cf 100644
--- a/src/kaleidoscope/plugin/Macros.h
+++ b/src/kaleidoscope/plugin/Macros.h
@@ -80,6 +80,7 @@ class Macros_ : public kaleidoscope::Plugin {
private:
Key lookupAsciiCode(uint8_t ascii_code);
+ bool isMacroKey(Key key);
};
}
diff --git a/src/kaleidoscope/plugin/Macros/MacroKeyDefs.h b/src/kaleidoscope/plugin/Macros/MacroKeyDefs.h
index a12cd038..ede4ce7a 100644
--- a/src/kaleidoscope/plugin/Macros/MacroKeyDefs.h
+++ b/src/kaleidoscope/plugin/Macros/MacroKeyDefs.h
@@ -16,9 +16,12 @@
#pragma once
-#define IS_MACRO B00100000
+#include "Kaleidoscope-Ranges.h"
+
+constexpr Key M(uint8_t n) {
+ return Key(kaleidoscope::ranges::MACRO_FIRST + n);
+}
-#define M(n) Key(n, KEY_FLAGS | SYNTHETIC | IS_MACRO)
#define Key_macroKey1 M(1)
#define Key_macroKey2 M(2)
#define Key_macroKey3 M(3)
diff --git a/tests/plugins/Macros/basic/sketch.ino b/tests/plugins/Macros/basic/sketch.ino
new file mode 100644
index 00000000..59c552ff
--- /dev/null
+++ b/tests/plugins/Macros/basic/sketch.ino
@@ -0,0 +1,69 @@
+/* -*- mode: c++ -*-
+ * Copyright (C) 2020 Keyboard.io, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ */
+
+#include
+#include
+
+// *INDENT-OFF*
+KEYMAPS(
+ [0] = KEYMAP_STACKED
+ (
+ M(0), M(1), M(255), ___, ___, ___, ___,
+ Key_X, ___, ___, ___, ___, ___, ___,
+ ___, ___, ___, ___, ___, ___,
+ ___, ___, ___, ___, ___, ___, ___,
+
+ ___, ___, ___, ___,
+ ___,
+
+ ___, ___, ___, ___, ___, ___, ___,
+ ___, ___, ___, ___, ___, ___, ___,
+ ___, ___, ___, ___, ___, ___,
+ ___, ___, ___, ___, ___, ___, ___,
+
+ ___, ___, ___, ___,
+ ___
+ ),
+)
+// *INDENT-ON*
+
+const macro_t *macroAction(uint8_t index, uint8_t key_state) {
+ if (keyToggledOn(key_state)) {
+ switch (index) {
+ case 0:
+ Kaleidoscope.hid().keyboard().pressKey(Key_A);
+ break;
+ case 1:
+ Kaleidoscope.hid().keyboard().pressKey(Key_B);
+ break;
+ case 255:
+ Kaleidoscope.hid().keyboard().pressKey(Key_C);
+ break;
+ }
+ }
+ return MACRO_NONE;
+}
+
+// Use Redial
+KALEIDOSCOPE_INIT_PLUGINS(Macros);
+
+void setup() {
+ Kaleidoscope.setup();
+}
+
+void loop() {
+ Kaleidoscope.loop();
+}
diff --git a/tests/plugins/Macros/basic/test/testcase.cpp b/tests/plugins/Macros/basic/test/testcase.cpp
new file mode 100644
index 00000000..e23c5d52
--- /dev/null
+++ b/tests/plugins/Macros/basic/test/testcase.cpp
@@ -0,0 +1,136 @@
+/* -*- mode: c++ -*-
+ * Copyright (C) 2020 Keyboard.io, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ */
+
+#include "testing/setup-googletest.h"
+
+SETUP_GOOGLETEST();
+
+namespace kaleidoscope {
+namespace testing {
+namespace {
+
+constexpr KeyAddr addr_macro_A{0, 0};
+constexpr KeyAddr addr_macro_B{0, 1};
+constexpr KeyAddr addr_macro_C{0, 2};
+constexpr KeyAddr addr_macro_X{0, 3};
+constexpr KeyAddr addr_X{1, 0};
+
+class MacrosBasic : public VirtualDeviceTest {
+ protected:
+ std::set expected_keycodes_ = {};
+ std::unique_ptr state_ = nullptr;
+};
+
+TEST_F(MacrosBasic, MacroIndex_0) {
+
+ sim_.Press(addr_macro_A);
+ state_ = RunCycle();
+ expected_keycodes_.insert(Key_A.getKeyCode());
+
+ ASSERT_EQ(state_->HIDReports()->Keyboard().size(), 1)
+ << "There should be one HID report";
+ EXPECT_THAT(state_->HIDReports()->Keyboard(0).ActiveKeycodes(),
+ ::testing::ElementsAreArray(expected_keycodes_))
+ << "The report should include only `A`";
+
+ sim_.Release(addr_macro_A);
+ state_ = RunCycle();
+ expected_keycodes_.erase(Key_A.getKeyCode());
+
+ ASSERT_EQ(state_->HIDReports()->Keyboard().size(), 1)
+ << "There should be one report after letter key release";
+ EXPECT_THAT(state_->HIDReports()->Keyboard(0).ActiveKeycodes(),
+ ::testing::ElementsAreArray(expected_keycodes_))
+ << "The report should be empty";
+}
+
+TEST_F(MacrosBasic, MacroIndex_1) {
+
+ sim_.Press(addr_macro_B);
+ state_ = RunCycle();
+ expected_keycodes_.insert(Key_B.getKeyCode());
+
+ ASSERT_EQ(state_->HIDReports()->Keyboard().size(), 1)
+ << "There should be one HID report";
+ EXPECT_THAT(state_->HIDReports()->Keyboard(0).ActiveKeycodes(),
+ ::testing::ElementsAreArray(expected_keycodes_))
+ << "The report should include only `B`";
+
+ sim_.Release(addr_macro_B);
+ state_ = RunCycle();
+ expected_keycodes_.erase(Key_B.getKeyCode());
+
+ ASSERT_EQ(state_->HIDReports()->Keyboard().size(), 1)
+ << "There should be one report after letter key release";
+ EXPECT_THAT(state_->HIDReports()->Keyboard(0).ActiveKeycodes(),
+ ::testing::ElementsAreArray(expected_keycodes_))
+ << "The report should be empty";
+}
+
+TEST_F(MacrosBasic, MacroIndex_255) {
+
+ sim_.Press(addr_macro_C);
+ state_ = RunCycle();
+ expected_keycodes_.insert(Key_C.getKeyCode());
+
+ ASSERT_EQ(state_->HIDReports()->Keyboard().size(), 1)
+ << "There should be one HID report";
+ EXPECT_THAT(state_->HIDReports()->Keyboard(0).ActiveKeycodes(),
+ ::testing::ElementsAreArray(expected_keycodes_))
+ << "The report should include only `C`";
+
+ state_ = RunCycle();
+ expected_keycodes_.erase(Key_C.getKeyCode());
+
+ ASSERT_EQ(state_->HIDReports()->Keyboard().size(), 1)
+ << "There should be one report in the next cycle";
+ EXPECT_THAT(state_->HIDReports()->Keyboard(0).ActiveKeycodes(),
+ ::testing::ElementsAreArray(expected_keycodes_))
+ << "The report should be empty";
+
+ sim_.Release(addr_macro_C);
+ state_ = RunCycle();
+
+ ASSERT_EQ(state_->HIDReports()->Keyboard().size(), 0)
+ << "There should be no report after release";
+}
+
+TEST_F(MacrosBasic, NonMacrosKey) {
+
+ sim_.Press(addr_X);
+ state_ = RunCycle();
+ expected_keycodes_.insert(Key_X.getKeyCode());
+
+ ASSERT_EQ(state_->HIDReports()->Keyboard().size(), 1)
+ << "There should be one HID report";
+ EXPECT_THAT(state_->HIDReports()->Keyboard(0).ActiveKeycodes(),
+ ::testing::ElementsAreArray(expected_keycodes_))
+ << "The report should include only `X`";
+
+ sim_.Release(addr_X);
+ state_ = RunCycle();
+ expected_keycodes_.erase(Key_X.getKeyCode());
+
+ ASSERT_EQ(state_->HIDReports()->Keyboard().size(), 1)
+ << "There should be one report after letter key release";
+ EXPECT_THAT(state_->HIDReports()->Keyboard(0).ActiveKeycodes(),
+ ::testing::ElementsAreArray(expected_keycodes_))
+ << "The report should be empty";
+}
+
+} // namespace
+} // namespace testing
+} // namespace kaleidoscope