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