Skip to content

Commit

Permalink
Implement tapKeySeq command. (case "Emoji Support")
Browse files Browse the repository at this point in the history
  • Loading branch information
kareltucek committed Aug 13, 2021
1 parent 6f1a011 commit ac0c0b2
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 36 deletions.
26 changes: 20 additions & 6 deletions MACRO_DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@ Similar command can be used to implement "loose gestures" - i.e., shortcuts wher

In the above examples, `tapKey` can be (and probably should be) replaced by `holdKey`. "Hold" activates the scancode for as long as the key is pressed while "tap" activates it just for a fraction of a second. This distinction may seem unimportant, but just as long as you don't try to play some games with it.

Complex key sequences can be achieved using `tapKeySeq`. For instance, following emoji macro - tap `thisMacro + s + h` (as shrug) to get shrugging person, or `thisMacro + s + w` to get sweaty smile.

$ifGesture 80 73 final tapKeySeq CS-u 1 f 6 0 5 space
$ifGesture 80 21 final tapKeySeq CS-u 1 f 9 3 7 space

You can simplify writing macros by using `#` and `@` characters. The first resolves a number as an index of a register. The second interprets the number as a relative action index. For instance the following macro will write out five "a"s with 50 ms delays

//you can comment your code via two slashes.
Expand Down Expand Up @@ -251,7 +256,8 @@ The following grammar is supported:
COMMAND = playMacro [<slot identifier (MACROID)>]
COMMAND = {startMouse|stopMouse} {move DIRECTION|scroll DIRECTION|accelerate|decelerate}
COMMAND = {setReg|addReg|subReg|mulReg} <register index (NUMBER)> <value (NUMBER)>
COMMAND = {pressKey|holdKey|tapKey|releaseKey} [sticky] SHORTCUT
COMMAND = {pressKey|holdKey|tapKey|releaseKey} SHORTCUT
COMMAND = tapKeySeq [SHORTCUT]+
COMMAND = set module.MODULEID.navigationMode.LAYERID NAVIGATIONMODE
COMMAND = set module.MODULEID.baseSpeed <speed multiplier part that always applies, 0-10.0 (FLOAT)>
COMMAND = set module.MODULEID.speed <speed multiplier part that is affected by acceleration, 0-10.0 (FLOAT)>
Expand All @@ -270,8 +276,8 @@ The following grammar is supported:
COMMAND = set debounceDelay <time in ms, at most 250 (NUMBER)>
COMMAND = set keystrokeDelay <time in ms, at most 65535 (NUMBER)>
COMMAND = set setEmergencyKey KEYID
CONDITION = {ifShortcut | ifNotShortcut} [IFSHORTCUTFLAGS]* [KEYID]*
CONDITION = {ifGesture | ifNotGesture} [IFSHORTCUTFLAGS]* [KEYID]*
CONDITION = {ifShortcut | ifNotShortcut} [IFSHORTCUTFLAGS]* [KEYID]+
CONDITION = {ifGesture | ifNotGesture} [IFSHORTCUTFLAGS]* [KEYID]+
CONDITION = {ifPrimary | ifSecondary}
CONDITION = {ifDoubletap | ifNotDoubletap}
CONDITION = {ifInterrupted | ifNotInterrupted}
Expand Down Expand Up @@ -299,8 +305,8 @@ The following grammar is supported:
CHAR = <any nonwhite ascii char>
KEYID = <id of hardware key obtained by resolveNextKeyId (NUMBER)>
LABEL = <string identifier>
SHORTCUT = MODMASK-KEY | KEY
MODMASK = [MODMASK]+ | [L|R]{S|C|A|G}
SHORTCUT = MODMASK- | MODMASK-KEY | KEY
MODMASK = [MODMASK]+ | [L|R]{S|C|A|G} | {p|r|h|t} | s
NAVIGATIONMODE = cursor | scroll | caret | media | none
MODULEID = trackball | touchpad | trackpoint | keycluster
KEY = CHAR|KEYABBREV
Expand Down Expand Up @@ -339,11 +345,19 @@ The following grammar is supported:
- `write <custom text>` will type rest of the string. Same as the plain text command. This is just easier to use with conditionals... If you want to interpolate register values, use (e.g.) `$setStatus Register 0 contains #0; $printStatus`.
- `writeExpr NUMBER` serves for writing out contents of registers or otherwise computed numbers. E.g., `$writeExpr #5` or `$writeExpr @-2`.
- `startMouse/stopMouse` start/stop corresponding mouse action. E.g., `startMouse move left`
- `pressKey|holdKey|tapKey|releaseKey` Presses/holds/taps/releases the provided scancode. E.g., `pressKey mouseBtnLeft`, `tapKey LC-v` (Left Control + (lowercase) v), `tapKey CS-f5` (Ctrl + Shift + F5).
- `pressKey|holdKey|tapKey|releaseKey` Presses/holds/taps/releases the provided scancode. E.g., `pressKey mouseBtnLeft`, `tapKey LC-v` (Left Control + (lowercase) v), `tapKey CS-f5` (Ctrl + Shift + F5), `LS-` (just tap left Shift).
- press means adding the scancode into a list of "active keys" and continuing the macro. The key is released once the macro ends. I.e., if the command is not followed by any sort of delay, the key will be released again almost immediately.
- release means removing the scancode from the list of "active keys". I.e., it negates effect of `pressKey` within the same macro. This does not affect scancodes emited by different keyboard actions.
- tap means pressing a key (more precisely, activating the scancode) and immediately releasing it again
- hold means pressing the key, waiting until key which activated the macro is released and then releasing the key again. I.e., `$holdKey <x>` is equivalent to `$pressKey <x>; $delayUntilRelease; $releaseKey <x>`, while `$tapKey <x>` is equivalent to `$pressKey <x>; $releaseKey <x>`.
- tapKeySeq can be used for executing custom sequences. Default action for each shortcut in sequence is tap. Other actions can be specified using `MODMASK`. E.g.:
- `CS-u 1 2 3 space` - control shift U + number + space - linux shortcut for custom unicode character.
- `pA- tab tab rA-` - tap alt tab twice to bring forward second background window.
- `MODMASK` meaning:
- `{S|C|A|G}` - Shift Control Alt Gui
- `[L|R]` - Left Right (which hand side modifier should be used)
- `s` - sticky
- `{p|r|h|t}` - press release hold tap - by default corresponds to the command used to invoke the sequence, but can be overriden for any.

### Control flow, macro execution (aka "functions"):

Expand Down
45 changes: 30 additions & 15 deletions right/src/macro_shortcut_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -361,50 +361,67 @@ static macro_action_t parseSingleChar(char c)
return action;
}

static uint8_t parseMods(const char* str, const char* strEnd)
static void parseMods(const char* str, const char* strEnd, macro_action_t* action)
{
const char* orig = str;
uint8_t modMask = 0;
bool left = true;
bool sticky = false;
macro_sub_action_t actionType = action->key.action;
while(str < strEnd) {
switch(*str) {
case 'l':
case 'L':
left = true;
break;
case 'r':
case 'R':
left = false;
break;
case 's':
case 'S':
modMask |= left ? HID_KEYBOARD_MODIFIER_LEFTSHIFT : HID_KEYBOARD_MODIFIER_RIGHTSHIFT;
left = true;
break;
case 'c':
case 'C':
modMask |= left ? HID_KEYBOARD_MODIFIER_LEFTCTRL : HID_KEYBOARD_MODIFIER_RIGHTCTRL;
left = true;
break;
case 'a':
case 'A':
modMask |= left ? HID_KEYBOARD_MODIFIER_LEFTALT : HID_KEYBOARD_MODIFIER_RIGHTALT;
left = true;
break;
case 'w':
case 'W':
case 'g':
case 'G':
modMask |= left ? HID_KEYBOARD_MODIFIER_LEFTGUI : HID_KEYBOARD_MODIFIER_RIGHTGUI;
left = true;
break;
case 's':
sticky = true;
break;
case 'p':
actionType = MacroSubAction_Press;
break;
case 'r':
actionType = MacroSubAction_Release;
break;
case 't':
actionType = MacroSubAction_Tap;
break;
case 'h':
actionType = MacroSubAction_Hold;
break;
default:
Macros_ReportError("Unrecognized mod abbreviation:", orig, strEnd);
break;
}
str++;
}
return modMask;

if (action->type != MacroActionType_Key && modMask != 0) {
Macros_ReportError("This action is not allowed to have modifiers!", str, strEnd);
} else {
action->key.modifierMask = modMask;
action->key.sticky = sticky;
}
action->key.action = actionType;
}

static lookup_record_t* lookup(uint8_t begin, uint8_t end, const char* str, const char* strEnd)
Expand Down Expand Up @@ -459,23 +476,21 @@ static macro_action_t parseAbbrev(const char* str, const char* strEnd)
return action;
}

macro_action_t MacroShortcutParser_Parse(const char* str, const char* strEnd)
macro_action_t MacroShortcutParser_Parse(const char* str, const char* strEnd, macro_sub_action_t type)
{
macro_action_t action;

if (FindChar('-', str, strEnd) == strEnd) {
//"-" notation not used
action = parseAbbrev(str, strEnd);
action.key.action = type;
}
else {
const char* delim = FindChar('-', str, strEnd);
action = parseAbbrev(delim+1, strEnd);
action.key.action = type;

if (action.type != MacroActionType_Key) {
Macros_ReportError("This action is not allowed to have modifiers!", str, strEnd);
}

action.key.modifierMask = parseMods(str, delim);
parseMods(str, delim, &action);
}
return action;
}
2 changes: 1 addition & 1 deletion right/src/macro_shortcut_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

void ShortcutParser_initialize();

macro_action_t MacroShortcutParser_Parse(const char* str, const char* strEnd);
macro_action_t MacroShortcutParser_Parse(const char* str, const char* strEnd, macro_sub_action_t type);
uint8_t MacroShortcutParser_CharacterToScancode(char character);
bool MacroShortcutParser_CharacterToShift(char character);
char MacroShortcutParser_ScancodeToCharacter(uint16_t scancode);
Expand Down
38 changes: 24 additions & 14 deletions right/src/macros.c
Original file line number Diff line number Diff line change
Expand Up @@ -1544,26 +1544,15 @@ static bool processIfSecondaryCommand(bool negate, const char* arg, const char*
return processCommand(arg, argEnd);
}

static macro_action_t decodeKey(const char* arg1, const char* argEnd)
static macro_action_t decodeKey(const char* arg1, const char* argEnd, macro_sub_action_t defaultSubAction)
{
macro_action_t action = MacroShortcutParser_Parse(arg1, TokEnd(arg1, argEnd));
macro_action_t action = MacroShortcutParser_Parse(arg1, TokEnd(arg1, argEnd), defaultSubAction);
return action;
}

static bool processKeyCommand(macro_sub_action_t type, const char* arg1, const char* argEnd)
{
bool isSticky = false;
if (TokenMatches(arg1, argEnd, "sticky")) {
isSticky = true;
arg1 = NextTok(arg1, argEnd);
}

macro_action_t action = decodeKey(arg1, argEnd);
action.key.action = type;

if (action.type == MacroActionType_Key) {
action.key.sticky = isSticky;
}
macro_action_t action = decodeKey(arg1, argEnd, type);

switch (action.type) {
case MacroActionType_Key:
Expand All @@ -1575,6 +1564,24 @@ static bool processKeyCommand(macro_sub_action_t type, const char* arg1, const c
}
}

static bool processTapKeySeqCommand(const char* arg1, const char* argEnd)
{
for(uint8_t i = 0; i < s->as.keySeqData.atKeyIdx; i++) {
arg1 = NextTok(arg1, argEnd);

if(arg1 == argEnd) {
s->as.keySeqData.atKeyIdx = 0;
return false;
};
}

if(!processKeyCommand(MacroSubAction_Tap, arg1, argEnd)) {
s->as.keySeqData.atKeyIdx++;
}

return true;
}

static bool processResolveNextKeyIdCommand()
{
postponeCurrentCycle();
Expand Down Expand Up @@ -2321,6 +2328,9 @@ static bool processCommand(const char* cmd, const char* cmdEnd)
else if (TokenMatches(cmd, cmdEnd, "tapKey")) {
return processKeyCommand(MacroSubAction_Tap, arg1, cmdEnd);
}
else if (TokenMatches(cmd, cmdEnd, "tapKeySeq")) {
return processTapKeySeqCommand(arg1, cmdEnd);
}
else {
goto failed;
}
Expand Down
3 changes: 3 additions & 0 deletions right/src/macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@
uint8_t layerIdx;

} holdLayerData;
struct {
uint8_t atKeyIdx;
} keySeqData;
};

//shared data
Expand Down

0 comments on commit ac0c0b2

Please sign in to comment.