2017-03-29 00:20:36 +02:00
/* Copyright 2016 Jack Humbert
*
* 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/>.
*/
2016-08-18 01:37:13 +02:00
/* Author: Wojciech Siewierski < wojciech dot siewierski at onet dot pl > */
2019-11-05 07:59:13 +01:00
# pragma once
/* Warn users that this is now deprecated and they should use the core feature instead. */
# pragma message "Dynamic Macros is now a core feature. See updated documentation to see how to configure it: https: //docs.qmk.fm/#/feature_dynamic_macros"
2016-08-18 01:37:13 +02:00
# include "action_layer.h"
# ifndef DYNAMIC_MACRO_SIZE
/* May be overridden with a custom value. Be aware that the effective
* macro length is half of this value : each keypress is recorded twice
* because of the down - event and up - event . This is not a bug , it ' s the
2016-10-09 12:52:39 +02:00
* intended behavior .
*
* Usually it should be fine to set the macro size to at least 256 but
* there have been reports of it being too much in some users ' cases ,
* so 128 is considered a safe default .
*/
2019-08-30 20:19:03 +02:00
# define DYNAMIC_MACRO_SIZE 128
2016-08-18 01:37:13 +02:00
# endif
/* Blink the LEDs to notify the user about some event. */
2019-08-30 20:19:03 +02:00
void dynamic_macro_led_blink ( void ) {
2017-05-05 00:11:24 +02:00
# ifdef BACKLIGHT_ENABLE
2016-08-18 01:37:13 +02:00
backlight_toggle ( ) ;
2017-09-30 14:25:48 +02:00
wait_ms ( 100 ) ;
2016-08-18 01:37:13 +02:00
backlight_toggle ( ) ;
2017-05-05 00:11:24 +02:00
# endif
2016-08-18 01:37:13 +02:00
}
2017-05-04 22:55:35 +02:00
/* Convenience macros used for retrieving the debug info. All of them
* need a ` direction ` variable accessible a t the call site .
*/
# define DYNAMIC_MACRO_CURRENT_SLOT() (direction > 0 ? 1 : 2)
2019-08-30 20:19:03 +02:00
# define DYNAMIC_MACRO_CURRENT_LENGTH(BEGIN, POINTER) ((int)(direction * ((POINTER) - (BEGIN))))
# define DYNAMIC_MACRO_CURRENT_CAPACITY(BEGIN, END2) ((int)(direction * ((END2) - (BEGIN)) + 1))
2017-05-04 22:55:35 +02:00
2016-08-18 01:37:13 +02:00
/**
* Start recording of the dynamic macro .
*
* @ param [ out ] macro_pointer The new macro buffer iterator .
* @ param [ in ] macro_buffer The macro buffer used to initialize macro_pointer .
*/
2019-08-30 20:19:03 +02:00
void dynamic_macro_record_start ( keyrecord_t * * macro_pointer , keyrecord_t * macro_buffer ) {
2017-05-04 22:55:35 +02:00
dprintln ( " dynamic macro recording: started " ) ;
2016-08-18 01:37:13 +02:00
dynamic_macro_led_blink ( ) ;
clear_keyboard ( ) ;
layer_clear ( ) ;
* macro_pointer = macro_buffer ;
}
/**
* Play the dynamic macro .
*
* @ param macro_buffer [ in ] The beginning of the macro buffer being played .
* @ param macro_end [ in ] The element after the last macro buffer element .
* @ param direction [ in ] Either + 1 or - 1 , which way to iterate the buffer .
*/
2019-08-30 20:19:03 +02:00
void dynamic_macro_play ( keyrecord_t * macro_buffer , keyrecord_t * macro_end , int8_t direction ) {
2017-05-04 22:55:35 +02:00
dprintf ( " dynamic macro: slot %d playback \n " , DYNAMIC_MACRO_CURRENT_SLOT ( ) ) ;
2016-08-18 01:37:13 +02:00
uint32_t saved_layer_state = layer_state ;
clear_keyboard ( ) ;
layer_clear ( ) ;
while ( macro_buffer ! = macro_end ) {
process_record ( macro_buffer ) ;
macro_buffer + = direction ;
}
clear_keyboard ( ) ;
layer_state = saved_layer_state ;
}
/**
* Record a single key in a dynamic macro .
*
2017-05-03 23:47:52 +02:00
* @ param macro_buffer [ in ] The start of the used macro buffer .
2016-08-18 01:37:13 +02:00
* @ param macro_pointer [ in , out ] The current buffer position .
2017-05-04 22:39:02 +02:00
* @ param macro2_end [ in ] The end of the other macro .
2016-08-18 01:37:13 +02:00
* @ param direction [ in ] Either + 1 or - 1 , which way to iterate the buffer .
* @ param record [ in ] The current keypress .
*/
2019-08-30 20:19:03 +02:00
void dynamic_macro_record_key ( keyrecord_t * macro_buffer , keyrecord_t * * macro_pointer , keyrecord_t * macro2_end , int8_t direction , keyrecord_t * record ) {
2017-05-03 23:47:52 +02:00
/* If we've just started recording, ignore all the key releases. */
if ( ! record - > event . pressed & & * macro_pointer = = macro_buffer ) {
2017-05-04 22:55:35 +02:00
dprintln ( " dynamic macro: ignoring a leading key-up event " ) ;
2017-05-03 23:47:52 +02:00
return ;
}
2017-05-04 22:39:02 +02:00
/* The other end of the other macro is the last buffer element it
* is safe to use before overwriting the other macro .
*/
2017-05-04 01:19:05 +02:00
if ( * macro_pointer - direction ! = macro2_end ) {
2016-08-18 01:37:13 +02:00
* * macro_pointer = * record ;
* macro_pointer + = direction ;
} else {
2017-05-04 00:58:01 +02:00
dynamic_macro_led_blink ( ) ;
2016-08-18 01:37:13 +02:00
}
2017-05-04 22:55:35 +02:00
2019-08-30 20:19:03 +02:00
dprintf ( " dynamic macro: slot %d length: %d/%d \n " , DYNAMIC_MACRO_CURRENT_SLOT ( ) , DYNAMIC_MACRO_CURRENT_LENGTH ( macro_buffer , * macro_pointer ) , DYNAMIC_MACRO_CURRENT_CAPACITY ( macro_buffer , macro2_end ) ) ;
2016-08-18 01:37:13 +02:00
}
/**
* End recording of the dynamic macro . Essentially just update the
* pointer to the end of the macro .
*/
2019-08-30 20:19:03 +02:00
void dynamic_macro_record_end ( keyrecord_t * macro_buffer , keyrecord_t * macro_pointer , int8_t direction , keyrecord_t * * macro_end ) {
2016-08-18 01:37:13 +02:00
dynamic_macro_led_blink ( ) ;
2017-05-04 01:37:46 +02:00
/* Do not save the keys being held when stopping the recording,
* i . e . the keys used to access the layer DYN_REC_STOP is on .
*/
2019-08-30 20:19:03 +02:00
while ( macro_pointer ! = macro_buffer & & ( macro_pointer - direction ) - > event . pressed ) {
2017-05-04 22:55:35 +02:00
dprintln ( " dynamic macro: trimming a trailing key-down event " ) ;
2017-05-04 01:37:46 +02:00
macro_pointer - = direction ;
}
2019-08-30 20:19:03 +02:00
dprintf ( " dynamic macro: slot %d saved, length: %d \n " , DYNAMIC_MACRO_CURRENT_SLOT ( ) , DYNAMIC_MACRO_CURRENT_LENGTH ( macro_buffer , macro_pointer ) ) ;
2017-05-04 22:55:35 +02:00
2016-08-18 01:37:13 +02:00
* macro_end = macro_pointer ;
}
/* Handle the key events related to the dynamic macros. Should be
* called from process_record_user ( ) like this :
*
* bool process_record_user ( uint16_t keycode , keyrecord_t * record ) {
* if ( ! process_record_dynamic_macro ( keycode , record ) ) {
* return false ;
* }
* < . . . THE REST OF THE FUNCTION . . . >
* }
*/
2019-08-30 20:19:03 +02:00
bool process_record_dynamic_macro ( uint16_t keycode , keyrecord_t * record ) {
2016-08-18 01:37:13 +02:00
/* Both macros use the same buffer but read/write on different
* ends of it .
*
* Macro1 is written left - to - right starting from the beginning of
* the buffer .
*
* Macro2 is written right - to - left starting from the end of the
* buffer .
*
* & macro_buffer macro_end
* v v
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2017-05-04 22:39:02 +02:00
* | > > > > > > MACRO1 > > > > > > < < < < < < < < < < < < < MACRO2 < < < < < < < < < < < < < |
2016-08-18 01:37:13 +02:00
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* ^ ^
* r_macro_end r_macro_buffer
*
* During the recording when one macro encounters the end of the
* other macro , the recording is stopped . Apart from this , there
* are no arbitrary limits for the macros ' length in relation to
* each other : for example one can either have two medium sized
* macros or one long macro and one short macro . Or even one empty
* and one using the whole buffer .
*/
static keyrecord_t macro_buffer [ DYNAMIC_MACRO_SIZE ] ;
/* Pointer to the first buffer element after the first macro.
* Initially points to the very beginning of the buffer since the
* macro is empty . */
static keyrecord_t * macro_end = macro_buffer ;
/* The other end of the macro buffer. Serves as the beginning of
* the second macro . */
static keyrecord_t * const r_macro_buffer = macro_buffer + DYNAMIC_MACRO_SIZE - 1 ;
/* Like macro_end but for the second macro. */
static keyrecord_t * r_macro_end = r_macro_buffer ;
/* A persistent pointer to the current macro position (iterator)
* used during the recording . */
static keyrecord_t * macro_pointer = NULL ;
/* 0 - no macro is being recorded right now
* 1 , 2 - either macro 1 or 2 is being recorded */
static uint8_t macro_id = 0 ;
if ( macro_id = = 0 ) {
/* No macro recording in progress. */
if ( ! record - > event . pressed ) {
switch ( keycode ) {
2019-08-30 20:19:03 +02:00
case DYN_REC_START1 :
dynamic_macro_record_start ( & macro_pointer , macro_buffer ) ;
macro_id = 1 ;
return false ;
case DYN_REC_START2 :
dynamic_macro_record_start ( & macro_pointer , r_macro_buffer ) ;
macro_id = 2 ;
return false ;
case DYN_MACRO_PLAY1 :
dynamic_macro_play ( macro_buffer , macro_end , + 1 ) ;
return false ;
case DYN_MACRO_PLAY2 :
dynamic_macro_play ( r_macro_buffer , r_macro_end , - 1 ) ;
return false ;
2016-08-18 01:37:13 +02:00
}
}
} else {
/* A macro is being recorded right now. */
switch ( keycode ) {
2019-08-30 20:19:03 +02:00
case DYN_REC_STOP :
/* Stop the macro recording. */
if ( record - > event . pressed ) { /* Ignore the initial release
* just after the recoding
* starts . */
switch ( macro_id ) {
case 1 :
dynamic_macro_record_end ( macro_buffer , macro_pointer , + 1 , & macro_end ) ;
break ;
case 2 :
dynamic_macro_record_end ( r_macro_buffer , macro_pointer , - 1 , & r_macro_end ) ;
break ;
}
macro_id = 0 ;
}
return false ;
case DYN_MACRO_PLAY1 :
case DYN_MACRO_PLAY2 :
dprintln ( " dynamic macro: ignoring macro play key while recording " ) ;
return false ;
default :
/* Store the key in the macro buffer and process it normally. */
2016-08-18 01:37:13 +02:00
switch ( macro_id ) {
2019-08-30 20:19:03 +02:00
case 1 :
dynamic_macro_record_key ( macro_buffer , & macro_pointer , r_macro_end , + 1 , record ) ;
break ;
case 2 :
dynamic_macro_record_key ( r_macro_buffer , & macro_pointer , macro_end , - 1 , record ) ;
break ;
2016-08-18 01:37:13 +02:00
}
2019-08-30 20:19:03 +02:00
return true ;
2016-08-18 01:37:13 +02:00
break ;
}
}
return true ;
}
2017-05-04 22:55:35 +02:00
# undef DYNAMIC_MACRO_CURRENT_SLOT
# undef DYNAMIC_MACRO_CURRENT_LENGTH
# undef DYNAMIC_MACRO_CURRENT_CAPACITY