Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions doc-dev/reference-manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,12 @@ COMMAND = set secondaryRole.defaultStrategy { simple | advanced }
COMMAND = set secondaryRole.advanced.timeout <ms, 0-500 (INT)>
COMMAND = set secondaryRole.advanced.timeoutAction { primary | secondary | none }
COMMAND = set secondaryRole.advanced.safetyMargin <ms, higher value adjusts sensitivity towards primary role -50-50 (INT)>
COMMAND = set secondaryRole.advanced.triggerByPress <trigger immediately on action key press (BOOL)>
COMMAND = set secondaryRole.advanced.triggerByRelease <trigger secondary role if action key is released before dual role (BOOL)>
COMMAND = set secondaryRole.advanced.triggeringEvent { press | release | none } <determines whether secondary role is triggered when another key is pressed or when another key is pressed and released while the secondary role key is being held, or not at all from other keys being pressed>
COMMAND = set secondaryRole.advanced.triggerByMouse <trigger secondary role immediately on mouse move (BOOL)
COMMAND = set secondaryRole.advanced.minimumHoldTime <ms, minimum time a key must be held before it can trigger secondary role 0-255 (INT)>
COMMAND = set secondaryRole.advanced.doubletapToPrimary <hold primary on doubletap (BOOL)>
COMMAND = set secondaryRole.advanced.doubletapTime <ms, 0-500 (INT)>
COMMAND = set secondaryRole.advanced.primaryFromSameHalf <prevents secondary role keys from triggering as secondary when the triggering key is from the same half of the keyboard (BOOL)>
COMMAND = set secondaryRole.advanced.acceptTriggersFromSameHalf <prevents secondary role keys from triggering as secondary from triggers from the same half of the keyboard (BOOL)>
COMMAND = set mouseKeys.{move|scroll}.initialSpeed <px/s, ~100/20 (INT)>
COMMAND = set mouseKeys.{move|scroll}.baseSpeed <px/s, ~800/20 (INT)>
COMMAND = set mouseKeys.{move|scroll}.initialAcceleration <px/s, ~1700/20 (INT)>
Expand Down Expand Up @@ -195,7 +194,7 @@ CONDITION = if (EXPRESSION)
CONDITION = else
CONDITION = {ifShortcut | ifNotShortcut} [IFSHORTCUT_OPTIONS]* [KEYID]+
CONDITION = {ifGesture | ifNotGesture} [IFSHORTCUT_OPTIONS]* [KEYID]+
CONDITION = {ifPrimary | ifSecondary} [ simpleStrategy | advancedStrategy | primaryFromSameHalf | primaryFromSameHalfDisabled ]
CONDITION = {ifPrimary | ifSecondary} [ simpleStrategy | advancedStrategy | ignoreTriggersFromSameHalf | acceptTriggersFromSameHalf ]
CONDITION = {ifHold | ifTap}
CONDITION = {ifDoubletap | ifNotDoubletap}
CONDITION = {ifInterrupted | ifNotInterrupted}
Expand Down Expand Up @@ -326,6 +325,8 @@ ZEPHYR_COMMAND = uhk { connections | mouseMultipliers | rollover BOOL | charger
COMMAND = set macroEngine.scheduler {blocking|preemptive}
COMMAND = set doubletapDelay <time in ms, at most 65535, alias to doubletapTimeout (INT)>
COMMAND = set modifierLayerTriggers.{control} {left|right|both}
COMMAND = set secondaryRole.advanced.triggerByPress <trigger immediately on action key press (BOOL)>
COMMAND = set secondaryRole.advanced.triggerByRelease <trigger secondary role if action key is released before dual role (BOOL)>
COMMAND = untoggleLayer
LAYERID = control
###########
Expand Down Expand Up @@ -482,7 +483,7 @@ Commands:
- `holdLayer LAYERID` mostly corresponds to the sequence `toggleLayer <layer>; delayUntilRelease; untoggleLayer`, except for more elaborate conflict resolution (releasing holds in incorrect order).
- `holdKeymapLayer KEYMAPID LAYERID` just as holdLayer, but allows referring to layer of different keymap. This reloads the entire keymap, so it may be very inefficient.
- `holdLayerMax/holdKeymapLayerMax` will timeout after <timeout> ms if no action is performed in that time.
- `ifPrimary/ifSecondary [ simpleStrategy | advancedStrategy | primaryFromSameHalf | primaryFromSameHalfDisabled ]... COMMAND` will wait until the firmware can distinguish whether primary or secondary action should be activated and then either execute `COMMAND` or skip it.
- `ifPrimary/ifSecondary [ simpleStrategy | advancedStrategy | ignoreTriggersFromSameHalf | acceptTriggersFromSameHalf ]... COMMAND` will wait until the firmware can distinguish whether primary or secondary action should be activated and then either execute `COMMAND` or skip it.

### Layer/Keymap loading manipulation / shared layers:

Expand All @@ -506,7 +507,7 @@ We allow postponing key activations in order to allow deciding between some scen
- `ifKeyPendingAt/ifNotKeyPendingAt <idx> <keyId>` looks into postponing queue at `idx`th waiting key and compares it to the `keyId`.
- `consumePending <n>` will remove n records from the queue.
- `activateKeyPostponed KEYID` will add tap of KEYID at the end of queue. If `atLayer LAYERID` is specified, action will be taken from that layer rather than current one. If `prepend` option is specified, event will be place at the beginning of the queue.
- `ifPrimary/ifSecondary [ simpleStrategy | advancedStrategy | primaryFromSameHalf | primaryFromSameHalfDisabled ] ... COMMAND` will wait until the firmware can distinguish whether primary or secondary action should be activated and then either execute `COMMAND` or skip it.
- `ifPrimary/ifSecondary [ simpleStrategy | advancedStrategy | ignoreTriggersFromSameHalf | acceptTriggersFromSameHalf ] ... COMMAND` will wait until the firmware can distinguish whether primary or secondary action should be activated and then either execute `COMMAND` or skip it.
- `ifHold/ifTap COMMAND` will wait until the key that activated the macro will be released or until holdTimeout elapses. Then it will either execute the command or skip it.
- `ifShortcut/ifNotShortcut/ifGesture/ifNotGesture [IFSHORTCUT_OPTIONS]* [KEYID]*` will wait for next keypresses until sufficient number of keys has been pressed. If the next keypresses correspond to the provided arguments (hardware ids), the keypresses are consumed and the condition is performed. Consuming takes place in both `if` and `ifNot` versions if the full list is matched. E.g., `ifShortcut 090 089 final tapKey C-V; holdKey v`.
- `Shortcut` requires continual press of keys (e.g., Ctrl+c). By default, it timeouts with the activation key release.
Expand Down Expand Up @@ -546,7 +547,7 @@ Conditions are checked before processing the rest of the command. If the conditi
- `ifRecording/ifNotRecording` and `ifRecordingId/ifNotRecordingId MACROID` test if the runtime macro recorder is in the recording state.
- `ifShortcut/ifNotShortcut [IFSHORTCUT_OPTIONS]* [KEYID]*` will wait for future keypresses and compare them to the argument. See the postponer mechanism section.
- `ifGesture/ifNotGesture [IFSHORTCUT_OPTIONS]* [KEYID]*` just as `ifShortcut`, but breaks after 1000ms instead of when the key is released. See the postponer mechanism section.
- `ifPrimary/ifSecondary [ simpleStrategy | advancedStrategy | primaryFromSameHalf | primaryFromSameHalfDisabled ] ... COMMAND` will wait until the firmware can distinguish whether primary or secondary action should be activated and then either execute `COMMAND` or skip it.
- `ifPrimary/ifSecondary [ simpleStrategy | advancedStrategy | ignoreTriggersFromSameHalf | acceptTriggersFromSameHalf ] ... COMMAND` will wait until the firmware can distinguish whether primary or secondary action should be activated and then either execute `COMMAND` or skip it.

### Modifiers

Expand Down Expand Up @@ -703,14 +704,13 @@ Key actions can be parametrized with macro arguments. These arguments can be exp
- advanced strategy may trigger secondary role depending on timeout, or depending on key release order.
- `set secondaryRole.advanced.timeout <timeout in ms, 350 (INT)>` if this timeout is reached, `timeoutAction` (secondary by default) role is activated.
- `set secondaryRole.advanced.timeoutAction { primary | secondary | none}` defines whether the primary action or the secondary role, or no action at all, should be activated when timeout is reached
- `set secondaryRole.advanced.triggerByRelease BOOL` if enabled, secondary role is chosen depending on the release order of the keys (`press-A, press-B, release-B, release-A` leads to secondary action; `press-A, press-B, release-A, release-B` leads to primary action). This is further modified by safetyMargin.
- `set secondaryRole.advanced.triggerByPress BOOL` if enabled, secondary role is triggered when there is another press, simiarly to the simple strategy. Unlike simple strategy, this allows setting timeout behaviors, and also is modified by safetyMargin.
- `set secondaryRole.advanced.triggeringEvent { press | release | none }` determines whether a secondary role key is triggered as secondary if another key is pressed while it's held, if another key is pressed and released while it's held, or not at all from other keys being held. The third option allows usage of the timeout feature without inter-key triggers.
- `set secondaryRole.advanced.triggerByMouse BOOL` if enabled, any mouse (module) activity triggers secondary role immediately.
- `set secondaryRole.advanced.minimumHoldTime <ms, 0-255 (INT)>` sets the minimum time that a key must be held before it is allowed to trigger as secondary role.
- `set secondaryRole.advanced.safetyMargin <ms, -50 - 50 (INT)>` finetunes sensitivity of the trigger-by-release and trigger-by-press behaviours, so that positive values favor primary role, while negative values favor secondary role. This works by adding the value to the action key (or subtracting from the dual role key). E.g., suppose trigger by release is active, and safetyMargin equal 50. Furthermore assume that dual-role key is released 30ms after the action key. Due to safety margin 50 being greater than 30, the dual-role key is still considered to be released first, and so primary role is activated.
- `set secondaryRole.advanced.doubletapToPrimary BOOL` allows switching timeout action to primary by doubletap to allow holding the primary action (Useful if you want a dual key on space.)
- `set secondaryRole.advanced.doubletapTime <ms, 200 (INT)>` configures the above timeout (measured press-to-press).
- `set secondaryRole.advanced.primaryFromSameHalf <(BOOL)>` enables or disables whether secondary role keys can trigger their secondary role if the triggering key is from the same keyboard half. `ifPrimary/ifSecondary` can specify explicitly whether to trigger primary from same half using the argument `primaryFromSameHalf/primaryFromSameHalfDisabled`.
- `set secondaryRole.advanced.acceptTriggersFromSameHalf <(BOOL)>` enables or disables whether secondary role keys can trigger their secondary role from key events from the same keyboard half. `ifPrimary/ifSecondary` can specify explicitly whether to allow secondary triggers from same half using the argument `ignoreTriggersFromSameHalf/acceptTriggersFromSameHalf`.

- `macroEngine`
- terminology:
Expand Down
16 changes: 7 additions & 9 deletions doc-dev/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ set secondaryRole.defaultStrategy advanced
set secondaryRole.advanced.timeout 350
set secondaryRole.advanced.timeoutAction secondary
set secondaryRole.advanced.safetyMargin 50
set secondaryRole.advanced.triggerByRelease 1
set secondaryRole.advanced.triggeringEvent release
set secondaryRole.advanced.doubletapToPrimary 0
```

Expand All @@ -456,7 +456,7 @@ set secondaryRole.defaultStrategy advanced
set secondaryRole.advanced.timeout 200
set secondaryRole.advanced.timeoutAction secondary
set secondaryRole.advanced.safetyMargin 0
set secondaryRole.advanced.triggerByRelease 0
set secondaryRole.advanced.triggeringEvent none
set secondaryRole.advanced.doubletapToPrimary 1
set secondaryRole.advanced.doubletapTime 200
```
Expand All @@ -468,8 +468,7 @@ set secondaryRole.defaultStrategy advanced
set secondaryRole.advanced.timeout 500
set secondaryRole.advanced.timeoutAction secondary
set secondaryRole.advanced.safetyMargin 0
set secondaryRole.advanced.triggerByRelease 0
set secondaryRole.advanced.triggerByPress 1
set secondaryRole.advanced.triggeringEvent press
set secondaryRole.advanced.doubletapToPrimary 1
set secondaryRole.advanced.doubletapTime 200
```
Expand All @@ -478,14 +477,14 @@ Firngrod's Home Row Mods (HRM) configuration (Recommended):

```
set secondaryRole.defaultStrategy advanced
set secondaryRole.advanced.triggerByRelease 1
set secondaryRole.advanced.triggeringEvent release
set secondaryRole.advanced.timeout 300
set secondaryRole.advanced.timeoutAction secondary
set secondaryRole.advanced.doubletapToPrimary 1
set secondaryRole.advanced.safetyMargin -100
set secondaryRole.advanced.triggerByMouse 1
set secondaryRole.advanced.minimumHoldTime 150
set secondaryRole.advanced.primaryFromSameHalf 1
set secondaryRole.advanced.acceptTriggersFromSameHalf 0
```

Full set of advanced strategy config values follows (for copy-paste convenience):
Expand All @@ -495,13 +494,12 @@ set secondaryRole.defaultStrategy advanced
set secondaryRole.advanced.timeout 500
set secondaryRole.advanced.timeoutAction secondary
set secondaryRole.advanced.safetyMargin 0
set secondaryRole.advanced.triggerByRelease 0
set secondaryRole.advanced.triggerByPress 0
set secondaryRole.advanced.triggeringEvent press
set secondaryRole.advanced.triggerByMouse 0
set secondaryRole.advanced.doubletapToPrimary 1
set secondaryRole.advanced.doubletapTime 200
set secondaryRole.advanced.minimumHoldTime 0
set secondaryRole.advanced.primaryFromSameHalf 0
set secondaryRole.advanced.acceptTriggersFromSameHalf 1
```

The above configuration will trigger the secondary role whenever the dual-role key is pressed for more than 200ms, i.e., just a very slightly prolonged activation will trigger the secondary role.
Expand Down
5 changes: 2 additions & 3 deletions right/src/config_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,12 @@ const config_t DefaultCfg = (config_t){
.SecondaryRoles_AdvancedStrategyDoubletapTimeout = 200,
.SecondaryRoles_AdvancedStrategyTimeout = 350,
.SecondaryRoles_AdvancedStrategySafetyMargin = 50,
.SecondaryRoles_AdvancedStrategyTriggerByRelease = true,
.SecondaryRoles_AdvancedStrategyTriggerByPress = false,
.SecondaryRoles_AdvancedStrategyTriggeringEvent = SecondaryRoleTriggeringEvent_Release,
.SecondaryRoles_AdvancedStrategyTriggerByMouse = false,
.SecondaryRoles_AdvancedStrategyMinimumHoldTime = 0,
.SecondaryRoles_AdvancedStrategyDoubletapToPrimary = true,
.SecondaryRoles_AdvancedStrategyTimeoutAction = SecondaryRoleState_Secondary,
.SecondaryRoles_AdvancedStrategyPrimaryFromSameHalf = false,
.SecondaryRoles_AdvancedStrategyAcceptTriggersFromSameHalf = true,
.SecondaryRoles_Strategy = SecondaryRoleStrategy_Simple,
.StickyModifierStrategy = Stick_Smart,
.Macros_Scheduler = Scheduler_Blocking,
Expand Down
5 changes: 2 additions & 3 deletions right/src/config_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,11 @@

// secondary roles
secondary_role_strategy_t SecondaryRoles_Strategy;
bool SecondaryRoles_AdvancedStrategyPrimaryFromSameHalf;
bool SecondaryRoles_AdvancedStrategyAcceptTriggersFromSameHalf;
uint16_t SecondaryRoles_AdvancedStrategyDoubletapTimeout;
uint16_t SecondaryRoles_AdvancedStrategyTimeout;
int16_t SecondaryRoles_AdvancedStrategySafetyMargin;
bool SecondaryRoles_AdvancedStrategyTriggerByRelease;
bool SecondaryRoles_AdvancedStrategyTriggerByPress;
secondary_role_triggering_event_t SecondaryRoles_AdvancedStrategyTriggeringEvent;
bool SecondaryRoles_AdvancedStrategyTriggerByMouse;
bool SecondaryRoles_AdvancedStrategyDoubletapToPrimary;
secondary_role_state_t SecondaryRoles_AdvancedStrategyTimeoutAction;
Expand Down
6 changes: 3 additions & 3 deletions right/src/config_parser/parse_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ parser_error_t parseConfig(config_buffer_t *buffer)
uint16_t secondaryRoles_AdvancedStrategyDoubletapTimeout = Cfg.SecondaryRoles_AdvancedStrategyDoubletapTimeout;
uint16_t secondaryRoles_AdvancedStrategyTimeout = Cfg.SecondaryRoles_AdvancedStrategyTimeout;
int16_t secondaryRoles_AdvancedStrategySafetyMargin = Cfg.SecondaryRoles_AdvancedStrategySafetyMargin;
bool secondaryRoles_AdvancedStrategyTriggerByRelease = Cfg.SecondaryRoles_AdvancedStrategyTriggerByRelease;
secondary_role_triggering_event_t secondaryRoles_AdvancedStrategyTriggeringEvent = Cfg.SecondaryRoles_AdvancedStrategyTriggeringEvent;
bool secondaryRoles_AdvancedStrategyDoubletapToPrimary = Cfg.SecondaryRoles_AdvancedStrategyDoubletapToPrimary;
serialized_secondary_role_action_type_t secondaryRoles_AdvancedStrategyTimeoutAction = SerializedSecondaryRoleActionType_Secondary;

Expand All @@ -191,7 +191,7 @@ parser_error_t parseConfig(config_buffer_t *buffer)
secondaryRoles_AdvancedStrategyDoubletapTimeout = ReadUInt16(buffer);
secondaryRoles_AdvancedStrategyTimeout = ReadUInt16(buffer);
secondaryRoles_AdvancedStrategySafetyMargin = ReadInt16(buffer);
secondaryRoles_AdvancedStrategyTriggerByRelease = ReadBool(buffer);
secondaryRoles_AdvancedStrategyTriggeringEvent = ReadBool(buffer) ? SecondaryRoleTriggeringEvent_Release : SecondaryRoleTriggeringEvent_Press;
secondaryRoles_AdvancedStrategyDoubletapToPrimary = ReadBool(buffer);
secondaryRoles_AdvancedStrategyTimeoutAction = ReadUInt8(buffer);

Expand Down Expand Up @@ -349,7 +349,7 @@ parser_error_t parseConfig(config_buffer_t *buffer)
Cfg.SecondaryRoles_AdvancedStrategyDoubletapTimeout = secondaryRoles_AdvancedStrategyDoubletapTimeout;
Cfg.SecondaryRoles_AdvancedStrategyTimeout = secondaryRoles_AdvancedStrategyTimeout;
Cfg.SecondaryRoles_AdvancedStrategySafetyMargin = secondaryRoles_AdvancedStrategySafetyMargin;
Cfg.SecondaryRoles_AdvancedStrategyTriggerByRelease = secondaryRoles_AdvancedStrategyTriggerByRelease;
Cfg.SecondaryRoles_AdvancedStrategyTriggeringEvent = secondaryRoles_AdvancedStrategyTriggeringEvent;
Cfg.SecondaryRoles_AdvancedStrategyDoubletapToPrimary = secondaryRoles_AdvancedStrategyDoubletapToPrimary;
switch (secondaryRoles_AdvancedStrategyTimeoutAction) {
case SerializedSecondaryRoleActionType_Primary:
Expand Down
9 changes: 9 additions & 0 deletions right/src/key_states.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,13 @@
static inline bool KeyState_DeactivatedEarlier(key_state_t* s) { return !s->previous && !s->current; };
static inline bool KeyState_NonZero(key_state_t* s) { return s->previous || s->current; };

static inline bool KeyState_IsRightHalf(key_state_t* s) { return s < &KeyStates[1][0]; };
static inline bool KeyState_IsLeftHalf(key_state_t* s) { return s < &KeyStates[2][0] && s >= &KeyStates[1][0]; };
static inline bool KeyState_IsKeyCluster(key_state_t* s) { return s >= &KeyStates[2][0] && s < &KeyStates[3][0]; };
static inline bool KeyState_IsMouseModule(key_state_t* s) { return s >= &KeyStates[3][0]; };
static inline bool KeyState_IsModule(key_state_t* s) { return s >= &KeyStates[2][0]; };
static inline bool KeyState_IsRightSide(key_state_t* s) { return KeyState_IsRightHalf(s) || KeyState_IsMouseModule(s); };
static inline bool KeyState_IsLeftSide(key_state_t* s) { return s >= &KeyStates[1][0] && s < &KeyStates[3][0]; };


#endif
8 changes: 4 additions & 4 deletions right/src/macros/commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -1006,11 +1006,11 @@ static macro_result_t processIfSecondaryCommand(parser_context_t* ctx, bool nega
strategy = SecondaryRoleStrategy_Advanced;
}

if (ConsumeToken(ctx, "primaryFromSameHalf")) {
fromSameHalf = SecondaryRole_PrimaryFromSameHalf;
if (ConsumeToken(ctx, "ignoreTriggersFromSameHalf")) {
fromSameHalf = SecondaryRole_IgnoreTriggersFromSameHalf;
}
else if (ConsumeToken(ctx, "primaryFromSameHalfDisabled")) {
fromSameHalf = SecondaryRole_PrimaryFromSameHalfDisabled;
else if (ConsumeToken(ctx, "acceptTriggersFromSameHalf")) {
fromSameHalf = SecondaryRole_AcceptTriggersFromSameHalf;
}
else {
break;
Expand Down
Loading