* Rip out old macro and action_function system * Update quantum/action_util.c Co-authored-by: Joel Challis <git@zvecr.com>pull/16027/head
@ -1,93 +0,0 @@ | |||
/* | |||
Copyright 2013 Jun Wako <wakojun@gmail.com> | |||
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, either version 2 of the License, or | |||
(at your option) any later version. | |||
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 <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include "action.h" | |||
#include "action_util.h" | |||
#include "action_macro.h" | |||
#include "wait.h" | |||
#ifdef DEBUG_ACTION | |||
# include "debug.h" | |||
#else | |||
# include "nodebug.h" | |||
#endif | |||
#ifndef NO_ACTION_MACRO | |||
# define MACRO_READ() (macro = MACRO_GET(macro_p++)) | |||
/** \brief Action Macro Play | |||
* | |||
* FIXME: Needs doc | |||
*/ | |||
void action_macro_play(const macro_t *macro_p) { | |||
macro_t macro = END; | |||
uint8_t interval = 0; | |||
if (!macro_p) return; | |||
while (true) { | |||
switch (MACRO_READ()) { | |||
case KEY_DOWN: | |||
MACRO_READ(); | |||
dprintf("KEY_DOWN(%02X)\n", macro); | |||
if (IS_MOD(macro)) { | |||
add_macro_mods(MOD_BIT(macro)); | |||
send_keyboard_report(); | |||
} else { | |||
register_code(macro); | |||
} | |||
break; | |||
case KEY_UP: | |||
MACRO_READ(); | |||
dprintf("KEY_UP(%02X)\n", macro); | |||
if (IS_MOD(macro)) { | |||
del_macro_mods(MOD_BIT(macro)); | |||
send_keyboard_report(); | |||
} else { | |||
unregister_code(macro); | |||
} | |||
break; | |||
case WAIT: | |||
MACRO_READ(); | |||
dprintf("WAIT(%u)\n", macro); | |||
{ | |||
uint8_t ms = macro; | |||
while (ms--) wait_ms(1); | |||
} | |||
break; | |||
case INTERVAL: | |||
interval = MACRO_READ(); | |||
dprintf("INTERVAL(%u)\n", interval); | |||
break; | |||
case 0x04 ... 0x73: | |||
dprintf("DOWN(%02X)\n", macro); | |||
register_code(macro); | |||
break; | |||
case 0x84 ... 0xF3: | |||
dprintf("UP(%02X)\n", macro); | |||
unregister_code(macro & 0x7F); | |||
break; | |||
case END: | |||
default: | |||
return; | |||
} | |||
// interval | |||
{ | |||
uint8_t ms = interval; | |||
while (ms--) wait_ms(1); | |||
} | |||
} | |||
} | |||
#endif |
@ -1,123 +0,0 @@ | |||
/* | |||
Copyright 2013 Jun Wako <wakojun@gmail.com> | |||
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, either version 2 of the License, or | |||
(at your option) any later version. | |||
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 <http://www.gnu.org/licenses/>. | |||
*/ | |||
#pragma once | |||
#include <stdint.h> | |||
#include "progmem.h" | |||
typedef uint8_t macro_t; | |||
#define MACRO_NONE (macro_t *)0 | |||
#define MACRO(...) \ | |||
({ \ | |||
static const macro_t __m[] PROGMEM = {__VA_ARGS__}; \ | |||
&__m[0]; \ | |||
}) | |||
#define MACRO_GET(p) pgm_read_byte(p) | |||
// Sends press when the macro key is pressed, release when release, or tap_macro when the key has been tapped | |||
#define MACRO_TAP_HOLD(record, press, release, tap_macro) (((record)->event.pressed) ? (((record)->tap.count <= 0 || (record)->tap.interrupted) ? (press) : MACRO_NONE) : (((record)->tap.count > 0 && !((record)->tap.interrupted)) ? (tap_macro) : (release))) | |||
// Holds down the modifier mod when the macro key is held, or sends macro instead when tapped | |||
#define MACRO_TAP_HOLD_MOD(record, macro, mod) MACRO_TAP_HOLD(record, (MACRO(D(mod), END)), MACRO(U(mod), END), macro) | |||
// Holds down the modifier mod when the macro key is held, or pressed a shifted key when tapped (eg: shift+3 for #) | |||
#define MACRO_TAP_SHFT_KEY_HOLD_MOD(record, key, mod) MACRO_TAP_HOLD_MOD(record, (MACRO(I(10), D(LSFT), T(key), U(LSFT), END)), mod) | |||
// Momentary switch layer when held, sends macro if tapped | |||
#define MACRO_TAP_HOLD_LAYER(record, macro, layer) \ | |||
(((record)->event.pressed) ? (((record)->tap.count <= 0 || (record)->tap.interrupted) ? ({ \ | |||
layer_on((layer)); \ | |||
MACRO_NONE; \ | |||
}) \ | |||
: MACRO_NONE) \ | |||
: (((record)->tap.count > 0 && !((record)->tap.interrupted)) ? (macro) : ({ \ | |||
layer_off((layer)); \ | |||
MACRO_NONE; \ | |||
}))) | |||
// Momentary switch layer when held, presses a shifted key when tapped (eg: shift+3 for #) | |||
#define MACRO_TAP_SHFT_KEY_HOLD_LAYER(record, key, layer) MACRO_TAP_HOLD_LAYER(record, MACRO(I(10), D(LSFT), T(key), U(LSFT), END), layer) | |||
#ifndef NO_ACTION_MACRO | |||
void action_macro_play(const macro_t *macro_p); | |||
#else | |||
# define action_macro_play(macro) | |||
#endif | |||
/* Macro commands | |||
* code(0x04-73) // key down(1byte) | |||
* code(0x04-73) | 0x80 // key up(1byte) | |||
* { KEY_DOWN, code(0x04-0xff) } // key down(2bytes) | |||
* { KEY_UP, code(0x04-0xff) } // key up(2bytes) | |||
* WAIT // wait milli-seconds | |||
* INTERVAL // set interval between macro commands | |||
* END // stop macro execution | |||
* | |||
* Ideas(Not implemented): | |||
* modifiers | |||
* system usage | |||
* consumer usage | |||
* unicode usage | |||
* function call | |||
* conditionals | |||
* loop | |||
*/ | |||
enum macro_command_id { | |||
/* 0x00 - 0x03 */ | |||
END = 0x00, | |||
KEY_DOWN, | |||
KEY_UP, | |||
/* 0x04 - 0x73 (reserved for keycode down) */ | |||
/* 0x74 - 0x83 */ | |||
WAIT = 0x74, | |||
INTERVAL, | |||
/* 0x84 - 0xf3 (reserved for keycode up) */ | |||
/* 0xf4 - 0xff */ | |||
}; | |||
/* TODO: keycode:0x04-0x73 can be handled by 1byte command else 2bytes are needed | |||
* if keycode between 0x04 and 0x73 | |||
* keycode / (keycode|0x80) | |||
* else | |||
* {KEY_DOWN, keycode} / {KEY_UP, keycode} | |||
*/ | |||
#define DOWN(key) KEY_DOWN, (key) | |||
#define UP(key) KEY_UP, (key) | |||
#define TYPE(key) DOWN(key), UP(key) | |||
#define WAIT(ms) WAIT, (ms) | |||
#define INTERVAL(ms) INTERVAL, (ms) | |||
/* key down */ | |||
#define D(key) DOWN(KC_##key) | |||
/* key up */ | |||
#define U(key) UP(KC_##key) | |||
/* key type */ | |||
#define T(key) TYPE(KC_##key) | |||
/* wait */ | |||
#define W(ms) WAIT(ms) | |||
/* interval */ | |||
#define I(ms) INTERVAL(ms) | |||
/* for backward comaptibility */ | |||
#define MD(key) DOWN(KC_##key) | |||
#define MU(key) UP(KC_##key) |
@ -1,88 +0,0 @@ | |||
/* Copyright 2017 Fred Sundvik | |||
* | |||
* 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, either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* 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 <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include "test_common.hpp" | |||
#include "time.h" | |||
using testing::InSequence; | |||
using testing::InvokeWithoutArgs; | |||
class Macro : public TestFixture {}; | |||
#define AT_TIME(t) WillOnce(InvokeWithoutArgs([current_time]() { EXPECT_EQ(timer_elapsed32(current_time), t); })) | |||
extern "C" const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) { | |||
if (record->event.pressed) { | |||
switch (id) { | |||
case 0: | |||
return MACRO(D(LSFT), T(H), U(LSFT), T(E), T(L), T(L), T(O), T(SPACE), W(100), D(LSFT), T(W), U(LSFT), I(10), T(O), T(R), T(L), T(D), D(LSFT), T(1), U(LSFT), END); | |||
} | |||
} | |||
return MACRO_NONE; | |||
}; | |||
TEST_F(Macro, PlayASimpleMacro) { | |||
TestDriver driver; | |||
InSequence s; | |||
auto key_macro = KeymapKey(0, 8, 0, M(0)); | |||
set_keymap({key_macro}); | |||
key_macro.press(); | |||
uint32_t current_time = timer_read32(); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT))).AT_TIME(0); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT, KC_H))).AT_TIME(0); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT))).AT_TIME(0); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).AT_TIME(0); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_E))).AT_TIME(0); | |||
// The macro system could actually skip these empty keyboard reports | |||
// it should be enough to just send a report with the next key down | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).AT_TIME(0); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_L))).AT_TIME(0); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).AT_TIME(0); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_L))).AT_TIME(0); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).AT_TIME(0); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_O))).AT_TIME(0); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).AT_TIME(0); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_SPACE))).AT_TIME(0); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).AT_TIME(0); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT))).AT_TIME(100); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT, KC_W))).AT_TIME(100); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT))).AT_TIME(100); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).AT_TIME(100); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_O))) | |||
// BUG: The timer should not really have advanced 10 ms here | |||
// See issue #1477 | |||
.AT_TIME(110); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())) | |||
// BUG: The timer should not advance on both keydown and key-up | |||
// See issue #1477 | |||
.AT_TIME(120); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_R))).AT_TIME(130); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).AT_TIME(140); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_L))).AT_TIME(150); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).AT_TIME(160); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_D))).AT_TIME(170); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).AT_TIME(180); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT))).AT_TIME(190); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT, KC_1))).AT_TIME(200); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT))).AT_TIME(210); | |||
EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).AT_TIME(220); | |||
run_one_scan_loop(); | |||
key_macro.release(); | |||
} |