Rip out old macro and action_function system (#16025)
* Rip out old macro and action_function system * Update quantum/action_util.c Co-authored-by: Joel Challis <git@zvecr.com>fix_template_bootmagic
parent
3340ca46e8
commit
1d11ae3087
@ -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(); |
|
||||||
} |
|
Loading…
Reference in new issue